From: Christian Marillat Date: Mon, 10 Aug 2026 18:36:30 +0000 (+0200) Subject: libtorrent-rasterbar (2.0.14-2) unstable; urgency=medium X-Git-Tag: archive/raspbian/2.0.14-2+rpi1^2~2 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=f73fa1a26c45f0f4a0789e1327fc139e0b147c45;p=libtorrent-rasterbar.git libtorrent-rasterbar (2.0.14-2) unstable; urgency=medium * Forgot to install git submodules. [dgit import unpatched libtorrent-rasterbar 2.0.14-2] --- f73fa1a26c45f0f4a0789e1327fc139e0b147c45 diff --cc debian/.gitlab-ci.yml index 0000000,0000000..845003c new file mode 100644 --- /dev/null +++ b/debian/.gitlab-ci.yml @@@ -1,0 -1,0 +1,9 @@@ ++image: registry.gitlab.com/eighthave/ci-image-git-buildpackage:latest ++ ++build: ++ artifacts: ++ paths: ++ - "*.deb" ++ expire_in: 1 day ++ script: ++ - gitlab-ci-git-buildpackage-all diff --cc debian/README.Debian index 0000000,0000000..a654481 new file mode 100644 --- /dev/null +++ b/debian/README.Debian @@@ -1,0 -1,0 +1,74 @@@ ++libtorrent-rasterbar for Debian ++------------------------------- ++ ++* libtorrent-rasterbar 0.14.2-2 ++ ++Revert build dependencies to boost1.37 only. ++ ++ -- Cristian Greco Wed, 11 Mar 2009 16:27:20 +0100 ++ ++* libtorrent-rasterbar 0.14.2-1 ++ ++Enabled building against boost1.37 due to the boost library transition: build ++dependencies will be updated each time a new version of boost get uploaded in ++Debian (this will happen likely two times a year in the future). ++Building against boost1.34.1 is still supported for backport purposes. ++ ++ -- Cristian Greco Fri, 27 Feb 2009 07:46:38 +0100 ++ ++ ++* libtorrent-rasterbar 0.14.1-1 ++ ++While version 0.14 was built against boost1.36, it seems like this release of ++the boost libraries has been deleted from Debian and maintainers encourage use ++of the next stable release boost1.37. ++Anyhow, the best choice actually is to build 0.14.1 against boost1.34.1 because ++it is a widely used and well tested release, and on the other hand maybe ++libtorrent-rasterbar is not yet ready to support boost1.37. ++ ++ -- Cristian Greco Mon, 15 Dec 2008 15:34:17 +0100 ++ ++ ++* libtorrent-rasterbar 0.14-1 ++ ++- Python bindings for libtorrent-rasterbar ++Added a patch (merged upstream, will be available in 0.14.1) to build python ++bindings using configure and make. The name of the python module (and package) ++is 'python-libtorrent' (as requested by upstream). ++ ++- Lintian Info tag 'no-symbols-control-file' ++Maintaining a symbols control file for that library is more a cost than a ++benefit. In fact, libtorrent-rasterbar exports a lot of symbols (quite big ++symbols files, ~600Kb) and the C++ ABI mangling requires one to maintain a ++different control file for each architecture. Moreover this library has a few ++reverse dependencies (less than 10 clients, much of them not yet packaged for ++Debian), so it wouldn't be difficult to maintain them. ++ ++ -- Cristian Greco Sun, 23 Nov 2008 18:55:24 +0100 ++ ++ ++* libtorrent-rasterbar 0.13.1-2 ++ ++Building a debug version (--enable-debug) of libtorrent-rasterbar seems to ++cause some clients to crash (such as qbittorrent, btg - qbittorrent needs a ++special option in order to build against a debug version), and actually most ++clients upstream developers seems to prefer a release build of the library. ++ ++Then, now libtorrent-rasterbar builds a release version (--disable-debug). ++ ++NOTE for maintainers of all clients which depend on libtorrent-rasterbar: ++please build-depends on this exact revision of the package in your ++debian/control file: ++ Build-Depends: libtorrent-rasterbar-dev (>= 0.13.1-2) ++ ++ -- Cristian Greco Wed, 23 Jul 2008 11:18:31 +0200 ++ ++ ++* libtorrent-rasterbar 0.13.1-1 ++ ++Actually, libtorrent-rasterbar has to be built against boost-1.34 and libasio ++instead of boost-1.35 (which includes libasio and conflicts with version 1.34); ++this is for compatibility with most bittorrent clients which depend also on ++boost-1.34. ++ ++ -- Cristian Greco Wed, 02 Jul 2008 10:46:21 +0200 diff --cc debian/changelog index 0000000,0000000..333bfa6 new file mode 100644 --- /dev/null +++ b/debian/changelog @@@ -1,0 -1,0 +1,841 @@@ ++libtorrent-rasterbar (2.0.14-2) unstable; urgency=medium ++ ++ * Forgot to install git submodules. ++ ++ -- Christian Marillat Mon, 10 Aug 2026 20:36:30 +0200 ++ ++libtorrent-rasterbar (2.0.14-1) unstable; urgency=medium ++ ++ * New upstream release. ++ ++ -- Christian Marillat Mon, 10 Aug 2026 17:27:22 +0200 ++ ++libtorrent-rasterbar (2.0.13-1) unstable; urgency=medium ++ ++ * New upstream release. ++ * openssl 4.0 support has been added (Closes: #1138465) ++ * Fix 'Fails to build source after successful build' (Closes: #1048692) ++ ++ -- Christian Marillat Tue, 09 Jun 2026 00:48:34 +0200 ++ ++libtorrent-rasterbar (2.0.12-1) unstable; urgency=medium ++ ++ * New upstream release. ++ * Update old fsf address in copyright. ++ ++ -- Christian Marillat Sat, 14 Mar 2026 11:32:56 +0100 ++ ++libtorrent-rasterbar (2.0.11-3) unstable; urgency=medium ++ ++ * Add libboost-dev as dependency for libtorrent-rasterbar-dev ++ (Closes: #1127426) ++ ++ -- Christian Marillat Mon, 09 Feb 2026 00:57:45 +0100 ++ ++libtorrent-rasterbar (2.0.11-2) unstable; urgency=medium ++ ++ * Remove libboost-system-dev from Build-Depends and from ++ libtorrent-rasterbar-dev Dependency (Closes: #1127230) ++ ++ -- Christian Marillat Fri, 06 Feb 2026 18:47:00 +0100 ++ ++libtorrent-rasterbar (2.0.11-1) unstable; urgency=medium ++ ++ * New upstream release. ++ * Should Build-Depends on a versioned libboost-system-dev '>= 1.81' ++ ++ -- Christian Marillat Tue, 28 Jan 2025 15:33:12 +0100 ++ ++libtorrent-rasterbar (2.0.10-1.1) unstable; urgency=medium ++ ++ * Non-maintainer upload. ++ * Rename libraries for 64-bit time_t transition. Closes: #1062684 ++ ++ -- Graham Inggs Wed, 28 Feb 2024 20:35:15 +0000 ++ ++libtorrent-rasterbar (2.0.10-1) unstable; urgency=medium ++ ++ * New upstream release. ++ * debian/control Replaces pkg-config by pkgconf ++ ++ -- Christian Marillat Mon, 19 Feb 2024 14:34:35 +0100 ++ ++libtorrent-rasterbar (2.0.9-2) unstable; urgency=medium ++ ++ * Fix 'Fails to build source after successful build' (Closes: #1048692) ++ ++ -- Christian Marillat Mon, 14 Aug 2023 07:25:12 +0200 ++ ++libtorrent-rasterbar (2.0.9-1) unstable; urgency=medium ++ ++ * New upstream release. ++ ++ -- Christian Marillat Tue, 30 May 2023 07:52:47 +0200 ++ ++libtorrent-rasterbar (2.0.8-1) unstable; urgency=medium ++ ++ * New upstream release. ++ ++ -- Christian Marillat Sun, 23 Oct 2022 17:10:21 +0200 ++ ++libtorrent-rasterbar (2.0.7-1) unstable; urgency=medium ++ ++ * New upstream release. ++ * Removed #1014795 hack, fixed upstream. ++ ++ -- Christian Marillat Sun, 17 Jul 2022 23:55:43 +0200 ++ ++libtorrent-rasterbar (2.0.6-5) unstable; urgency=medium ++ ++ * Restore hack to fix #1014795 ++ ++ -- Christian Marillat Tue, 12 Jul 2022 17:08:26 +0200 ++ ++libtorrent-rasterbar (2.0.6-4) unstable; urgency=medium ++ ++ * Build with latest boost1.74 (Closes: #1013931) ++ ++ -- Christian Marillat Mon, 04 Jul 2022 12:40:15 +0200 ++ ++libtorrent-rasterbar (2.0.6-3) unstable; urgency=medium ++ ++ * debian/rules Add hack to fix broken pkg-config file (Closes: #1009875) ++ ++ -- Christian Marillat Tue, 19 Apr 2022 20:41:48 +0200 ++ ++libtorrent-rasterbar (2.0.6-2) unstable; urgency=medium ++ ++ * debian/rules don't call dh_auto_install-indep ++ ++ -- Christian Marillat Sun, 17 Apr 2022 14:31:55 +0200 ++ ++libtorrent-rasterbar (2.0.6-1) unstable; urgency=medium ++ ++ * New upstream release. ++ * Add -indep rules to build the -doc package. ++ ++ -- Christian Marillat Sun, 17 Apr 2022 09:29:18 +0200 ++ ++libtorrent-rasterbar (2.0.5-6) unstable; urgency=medium ++ ++ * Remove unneeded ConvertUTF.cpp/.h in debian/copyright (CLoses: #1003423) ++ ++ -- Christian Marillat Sat, 26 Feb 2022 15:39:40 +0100 ++ ++libtorrent-rasterbar (2.0.5-5) unstable; urgency=medium ++ ++ * Well, really fix bug #1004702 ++ ++ -- Christian Marillat Tue, 01 Feb 2022 15:13:18 +0100 ++ ++libtorrent-rasterbar (2.0.5-4) unstable; urgency=medium ++ ++ * debian/control replace python3-all-dev by python3-dev (Closes: #1004702) ++ ++ -- Christian Marillat Tue, 01 Feb 2022 01:09:11 +0100 ++ ++libtorrent-rasterbar (2.0.5-3) unstable; urgency=medium ++ ++ * Build for unstable. ++ ++ -- Christian Marillat Sun, 30 Jan 2022 19:29:05 +0100 ++ ++libtorrent-rasterbar (2.0.5-2) experimental; urgency=medium ++ ++ * debian/copyright add simulation/libsimulator/* ++ ++ -- Christian Marillat Sun, 23 Jan 2022 13:10:13 +0100 ++ ++libtorrent-rasterbar (2.0.5-1) unstable; urgency=medium ++ ++ * New maintainer (Closes: #995076) ++ * New upstream release (CLoses: #969780) ++ * Fix python issues (Closes: #992575, #856600) ++ * Fix security issues (Closes: #987120) ++ * Fix vlc-plugin-bittorrent crash (Closes: #916399) ++ * Fix deluge crash (Closes: #999422) ++ ++ -- Christian Marillat Fri, 17 Dec 2021 11:21:19 +0100 ++ ++libtorrent-rasterbar (2.0.4-1) experimental; urgency=medium ++ ++ * New maintainer (Closes: #995076) ++ * Bump Standards-Version to 4.6.0 (No change) ++ * Upgrade to debhelper-compat 13 and dh-sequence-python3 ++ * Remove unneeded libtorrent-rasterbar-dbg package. ++ * debian/rules Don't use 'dh_makeshlibs -V' now the default with debhelper 13 ++ * Build with cmake and ninja ++ ++ -- Christian Marillat Thu, 30 Sep 2021 14:53:12 +0200 ++ ++libtorrent-rasterbar (1.2.14-1) unstable; urgency=medium ++ ++ [ Debian Janitor ] ++ * Apply multi-arch hints. ++ + libtorrent-rasterbar-dbg, libtorrent-rasterbar-dev, ++ libtorrent-rasterbar10: Add Multi-Arch: same. ++ + libtorrent-rasterbar-doc: Add Multi-Arch: foreign. ++ ++ [ Andrew Starr-Bochicchio ] ++ * New upstream release. ++ * Drop debian/patches/0001-python-move-storage.patch: ++ - Applied upstream. ++ * Stop building python3-libtorrent-dbg (Closes: #994306). ++ * debian/control: Add build depends on python3-setuptools and ++ libboost-tools-dev. ++ ++ -- Andrew Starr-Bochicchio Sat, 25 Sep 2021 11:40:11 -0400 ++ ++libtorrent-rasterbar (1.2.9-0.3) unstable; urgency=medium ++ ++ * Non-maintainer upload. ++ * Added d/patches/ README suggesting patch ordering and naming. ++ * Added patch from upstream to make python move_storage() match ++ C++ signature (Closes: #987306). ++ ++ -- Petter Reinholdtsen Fri, 30 Apr 2021 07:51:42 +0200 ++ ++libtorrent-rasterbar (1.2.9-0.2) unstable; urgency=medium ++ ++ * Non-maintainer upload. ++ * Fix building Python bindings with -std=c++14 (Closes: #975953), idea from ++ Holger Hoffstätte, https://bugs.gentoo.org/739654. ++ ++ -- Andrey Rahmatullin Sat, 05 Dec 2020 16:49:55 +0500 ++ ++libtorrent-rasterbar (1.2.9-0.1) unstable; urgency=medium ++ ++ * Non-maintainer upload. ++ ++ [ Andrew Starr-Bochicchio ] ++ * New upstream release. ++ - Removes src/ConvertUTF.cpp (Closes: #956285). ++ * debian/copyright: Address issues found in copyright ++ review. ++ * debian/control: ++ - Remove duplicated python3-docutils build-dep. ++ - Drop python build-dep (Closes: #967169, #936927). ++ * debian/rules: ++ - Prevent call to pyversions by using pybuild (#962090). ++ - Pass --std=c++14 ++ ++ -- Jonas Smedegaard Wed, 25 Nov 2020 09:34:56 +0100 ++ ++libtorrent-rasterbar (1.2.5-1.2) unstable; urgency=medium ++ ++ * Non-maintainer upload ++ * Link against libatomic on riscv64 as well ++ ++ -- Graham Inggs Tue, 23 Jun 2020 18:11:28 +0000 ++ ++libtorrent-rasterbar (1.2.5-1.1) unstable; urgency=high ++ ++ * Non-maintainer upload. ++ * Link against libatomic on armel/mipsel/m68k/powerpc/sh4. ++ (Closes: #958958) ++ * Build depend on python, which reopens #936927 but closes: #962090. ++ ++ -- Adrian Bunk Wed, 10 Jun 2020 17:37:20 +0300 ++ ++libtorrent-rasterbar (1.2.5-1) unstable; urgency=medium ++ ++ * New upstream release (Closes: #952447). ++ * debian/control: ++ - Depend on python3-docutils (Closes: #936927). ++ - Bump Standards-Version to 4.5.0. ++ * New package libtorrent-rasterbar10, bump library soname. ++ ++ -- Andrew Starr-Bochicchio Mon, 09 Mar 2020 20:41:03 -0400 ++ ++libtorrent-rasterbar (1.1.13-1.1) unstable; urgency=medium ++ ++ * Non-maintainer upload. ++ * Stop building the Python2 packages. Closes: #936927. ++ * Fix installation of the python3.8 extension. Closes: #945473. ++ ++ -- Matthias Klose Mon, 25 Nov 2019 17:42:44 +0100 ++ ++libtorrent-rasterbar (1.1.13-1) unstable; urgency=medium ++ ++ * New upstream version 1.1.13 ++ * Team upload. ++ ++ -- Ondřej Surý Thu, 11 Jul 2019 09:18:04 +0200 ++ ++libtorrent-rasterbar (1.1.11-2) unstable; urgency=medium ++ ++ * d/rules: Use boost-python >=1.67 soname suffix to fix ++ Python 3 linking (LP: #1800264). ++ ++ -- Calum Lind Sat, 08 Dec 2018 21:49:08 -0500 ++ ++libtorrent-rasterbar (1.1.11-1) unstable; urgency=medium ++ ++ [ Ondřej Nový ] ++ * d/control: Remove ancient X-Python-Version field ++ * d/control: Remove ancient X-Python3-Version field ++ ++ [ Ondřej Surý ] ++ * New upstream version 1.1.11 ++ * Team upload. ++ ++ -- Ondřej Surý Sun, 02 Dec 2018 09:38:41 +0000 ++ ++libtorrent-rasterbar (1.1.9-1) unstable; urgency=medium ++ ++ [ Unit 193 ] ++ * New upstream release (Closes: #900975). ++ - d/p/python-dbg-build-fix.patch: Drop, wasn't in series, fixed upstream. ++ - d/p/fix-html-docs.patch: Refresh and extend patch. ++ * d/compat, d/control: Bump dh compat to 11. ++ * d/control: ++ - Add build-depend on dh-python. ++ - Drop versioned build-depend on dpkg-dev. ++ - Drop Pre-Depends on dpkg (Was for xz support.) ++ - Update homepage to https://libtorrent.org/ (Closes: #900946). ++ - Update Vcs-* for Salsa. ++ - Fix python3-libtorrent-dbg depend: ++ python-libtorrent → python3-libtorrent (Closes: #906078). ++ * d/control, d/copyright, d/rules: ++ Upstream dropped geoip support, drop references. ++ * d/copyright: ++ - Update ConvertUTF.(cpp,h) license. ++ - Update 'format' field to use https. ++ * d/rules: ++ - Drop --parallel, autoreconf, and xz compression. These are now default. ++ * Bump Standards-Version to 4.1.4. ++ * Remove trailing whitespaces under debian/* ++ ++ [ Andrew Starr-Bochicchio ] ++ * New upstream release. ++ ++ -- Andrew Starr-Bochicchio Sat, 22 Sep 2018 20:54:52 -0400 ++ ++libtorrent-rasterbar (1.1.5-1) unstable; urgency=medium ++ ++ * New upstream release. ++ * Bump Standards-Version 4.1.2, no changes. ++ ++ -- Andrew Starr-Bochicchio Mon, 25 Dec 2017 14:29:03 -0500 ++ ++libtorrent-rasterbar (1.1.4-1) unstable; urgency=medium ++ ++ * New upstream release (Closes: #857239). ++ - Fix file rename issue with name prefix matching ++ torrent name (Closes: #874136). ++ - Fix ABI compatibility issue introduced with ++ preformatted entry type (Closes: #842538). ++ * debian/libtorrent-rasterbar-doc.install: ++ - Adjust for new release. ++ * debian/control: ++ - All priority 'extra' packages are now 'optional.' ++ - Bump Standards-Version to 4.1.0. ++ - Drop un-needed build dep on dh-autoreconf. ++ ++ -- Andrew Starr-Bochicchio Sun, 03 Sep 2017 13:28:04 -0400 ++ ++libtorrent-rasterbar (1.1.1-1) unstable; urgency=medium ++ ++ * New upstream release. ++ * Drop remove-msse4.2.patch, fix-get-ip-filter.patch, ++ 1.1.0-python-fixes.patch, and fix-invalid-input-crash.patch. ++ All applied upstream. ++ * Bump debian/compat to 10. ++ * Adjust install files for multi-arch. ++ ++ -- Andrew Starr-Bochicchio Sat, 29 Oct 2016 16:39:13 -0400 ++ ++libtorrent-rasterbar (1.1.0-3) unstable; urgency=medium ++ ++ * fix-get-ip-filter.patch: Backport upstream fix for ++ crash in session::get_ip_filter (Closes: #834630). ++ * 1.1.0-python-fixes.patch: Backport a number of upstream ++ commits with fixes for the Python bindings. ++ * fix-invalid-input-crash.patch: Backport upstream commit ++ fixing crash on invalid input in http_parser. ++ ++ -- Andrew Starr-Bochicchio Tue, 23 Aug 2016 20:50:44 -0400 ++ ++libtorrent-rasterbar (1.1.0-2) unstable; urgency=medium ++ ++ * Fix builds on arches where the multiarch tuple (i386-*) and ++ the GNU tuple (i686-*) differ (Closes: #833944). ++ * remove-msse4.2.patch: Backport upstream commit removing ++ -msse4.2 commandline option from configure script (Closes: #833944) ++ ++ -- Andrew Starr-Bochicchio Wed, 10 Aug 2016 20:23:29 -0400 ++ ++libtorrent-rasterbar (1.1.0-1) unstable; urgency=medium ++ ++ * New upstream release. ++ - Fixes CVE-2016-5301, crash while parsing invalid ++ chunked HTTP or UPnP response (Closes: #826380). ++ * New package libtorrent-rasterbar9, bump library soname. ++ * Drop python-clean-without-bjam.patch, applied upstream. ++ * Drop missing_rst_file.patch, applied upstream. ++ * Build depend on libboost-chrono-dev and libboost-random-dev. ++ * python-dbg-build-fix.patch: Fix building bindings with python-dbg. ++ Thanks to Calum Lind. ++ * Bump Standards-Version to 3.9.8, no changes. ++ * Build with dh_autoreconf. ++ ++ -- Andrew Starr-Bochicchio Mon, 08 Aug 2016 23:38:37 -0400 ++ ++libtorrent-rasterbar (1.0.7-1) unstable; urgency=medium ++ ++ * New upstream release (Closes: #806396). ++ * Update Source link in debian/copyright. ++ * debian/patches/missing_rst_file.patch: Add file missing ++ from upstream distribution. ++ ++ -- Andrew Starr-Bochicchio Fri, 27 Nov 2015 12:41:42 -0500 ++ ++libtorrent-rasterbar (1.0.6-2) unstable; urgency=medium ++ ++ * Upload to unstable (Closes: #791176). ++ ++ -- Andrew Starr-Bochicchio Sat, 29 Aug 2015 12:55:09 -0400 ++ ++libtorrent-rasterbar (1.0.6-1) experimental; urgency=medium ++ ++ * New upstream release (Closes: #785676). ++ - Bump library soname. ++ * Drop fix-python-build-missing-byteshpp.patch, applied upstream. ++ * python-clean-without-bjam.patch: Only use bjam when '--bjam' ++ is passed explicitly. ++ * Bump Standards-Version to 3.9.6, no changes. ++ * Point debian/watch at GitHub. ++ ++ -- Andrew Starr-Bochicchio Sat, 01 Aug 2015 18:03:37 -0400 ++ ++libtorrent-rasterbar (0.16.18-1) unstable; urgency=medium ++ ++ * New upstream version. ++ - fix string handling in python3 bindings (Closes: #756503). ++ * debian/patches: ++ - fix-python-dbg-build.patch: drop, fixed upstream. ++ - fix-html-docs.patch: refresh. ++ - fix-python-build-missing-byteshpp.patch: new, fix missing file ++ from upstream tarball. ++ ++ -- Cristian Greco Sat, 18 Oct 2014 19:47:37 +0200 ++ ++libtorrent-rasterbar (0.16.17-2) unstable; urgency=medium ++ ++ * debian/control: switch back to unversioned {build-,}depends ++ for boost libraries. (Closes: #759387) ++ ++ -- Cristian Greco Tue, 02 Sep 2014 22:48:17 +0200 ++ ++libtorrent-rasterbar (0.16.17-1) unstable; urgency=medium ++ ++ * New upstream release. ++ - Don't fall back on wildcard port in UPnP (LP: #1330703). ++ ++ -- Andrew Starr-Bochicchio Sun, 22 Jun 2014 11:36:34 -0400 ++ ++libtorrent-rasterbar (0.16.16-1) unstable; urgency=medium ++ ++ * New upstream release. ++ * Update watch file to find new releases of sourceforge. ++ ++ -- Andrew Starr-Bochicchio Sat, 07 Jun 2014 15:39:40 -0400 ++ ++libtorrent-rasterbar (0.16.13-1) unstable; urgency=medium ++ ++ * New upstream release. ++ * Add myself as an uploader. ++ * Bump Standards-Version to 3.9.5, no changes needed. ++ * Update fix-html-docs.patch so that all docs are built from ++ source and fix lintian privacy-breach-google-adsense errors. ++ ++ -- Andrew Starr-Bochicchio Mon, 24 Feb 2014 14:11:14 -0500 ++ ++libtorrent-rasterbar (0.16.11-2.1) unstable; urgency=medium ++ ++ * Non-maintainer upload. ++ * Explicitly pass --with-boost-system=boost_system to configure ++ as ax_boost_system.m4 doesn't want to find it now that it ++ is multiarch. Fixes FTBFS (Closes: #738381). ++ * Fix build with python3.4 as a supported version, using python-config. ++ ++ -- Andrew Starr-Bochicchio Mon, 17 Feb 2014 12:19:53 -0500 ++ ++libtorrent-rasterbar (0.16.11-2) unstable; urgency=low ++ ++ * debian/control: libtorrent-rasterbar-dev should depend on ++ boost-system1.54 (Closes: #726823). ++ ++ -- Cristian Greco Sat, 19 Oct 2013 20:26:01 +0200 ++ ++libtorrent-rasterbar (0.16.11-1) unstable; urgency=low ++ ++ * Non-maintainer upload. ++ * New upstream version 0.16.11. ++ - fixes build with Boost 1.54 (Closes: #724708). ++ * fix-ftbfs-kfreebsd-iconv.patch: deleted (merged upstream). ++ * debian/rules: fix linking with boost_python libraries (Closes: #721546). ++ * debian/control: ++ - switch to boost1.54-dev | boost-dev deps. ++ - bump Standards-Version to 3.9.4 (no changes required). ++ * debian/copyright: ++ - update in according to Copyright format 1.0 ++ - remove copyright holders whose code was deleted from source tree ++ - add missed copyright holders ++ - update years ++ ++ -- Boris Pek Sat, 28 Sep 2013 20:27:56 +0300 ++ ++libtorrent-rasterbar (0.16.10-3.1) unstable; urgency=low ++ ++ * Non-maintainer upload. ++ * Adjust python3-libtorrent.install to fix FTBFS when not built with ++ python3.2 support (Closes: #718666) ++ ++ -- Scott Kitterman Sat, 03 Aug 2013 22:24:08 -0400 ++ ++libtorrent-rasterbar (0.16.10-3) unstable; urgency=low ++ ++ * debian/patches: ++ - fix-ftbfs-kfreebsd-iconv.patch: new patch, fix ++ ftbfs on kfreebsd, thanks Pino Toscano! (Closes: #712797) ++ - refresh other patches. ++ ++ -- Cristian Greco Thu, 20 Jun 2013 21:18:23 +0200 ++ ++libtorrent-rasterbar (0.16.10-2) unstable; urgency=low ++ ++ * Upload to unstable. ++ - fixes all ftbfs (Closes: #701311, #710624). ++ * debian/control: switch to conditional build-dep on ++ boost1.53-dev | boost-dev. ++ ++ -- Cristian Greco Mon, 17 Jun 2013 22:56:07 +0200 ++ ++libtorrent-rasterbar (0.16.10-1) experimental; urgency=low ++ ++ * New upstream version 0.16.10. ++ * Build python3 packages (patch by Andrew Starr-Bochicchio ++ ) (Closes: #708072). ++ * debian/rules: add explicit configure arg --with-libiconv. ++ ++ -- Cristian Greco Sun, 26 May 2013 13:57:13 +0200 ++ ++libtorrent-rasterbar (0.16.9-1) experimental; urgency=low ++ ++ * New upstream version 0.16.9. ++ - new package libtorrent-rasterbar7 (bump up library soname). ++ - debian/control: drop now useless (build-)deps. ++ - debian/control: switch to explicit boost1.53 (build-)deps. ++ - upload to experimental: the library is pretty stable, but will be ++ uploaded to unstable only after the transition of boost1.53. ++ ++ -- Cristian Greco Sun, 12 May 2013 10:48:57 +0200 ++ ++libtorrent-rasterbar (0.15.10-1) unstable; urgency=low ++ ++ * New upstream version. ++ ++ -- Cristian Greco Tue, 06 Mar 2012 21:27:49 +0100 ++ ++libtorrent-rasterbar (0.15.9-1) unstable; urgency=low ++ ++ * New upstream version. ++ * debian/{control,rules}: compress binary packages with xz and add ++ Pre-Depends on dpkg (>= 1.15.6~). ++ * debian/rules: enable default hardening options from dpkg-buildflags. ++ ++ -- Cristian Greco Wed, 21 Dec 2011 13:08:22 +0100 ++ ++libtorrent-rasterbar (0.15.8-1) unstable; urgency=low ++ ++ * New upstream version. ++ - fix-webseeds-r5915.patch: drop patch, fixed upstream. ++ - refreshed other patches. ++ ++ -- Cristian Greco Fri, 30 Sep 2011 13:21:21 +0200 ++ ++libtorrent-rasterbar (0.15.7-1) unstable; urgency=low ++ ++ * New upstream version. ++ - fix-webseeds-r5915.patch: new patch, fix a web seeds bug caused ++ by a wrong merge commit. ++ * debian/control: ++ - use my @debian.org address. ++ - drop DMUA field. ++ ++ -- Cristian Greco Fri, 19 Aug 2011 18:47:49 +0200 ++ ++libtorrent-rasterbar (0.15.6-2) unstable; urgency=low ++ ++ * debian/control: switch to dh_python2 for the python bindings. ++ * debian/rules: no need to pass -a to dh_strip and dh_makeshlibs. ++ * debian/patches: ++ - fix-html-docs.patch: renamed, added dep3 tags. ++ - fix-python-dbg-build.patch: new patch, fix wrong configure check ++ when building with pythonX-dbg. ++ * debian/{control,rules}: introducing a new python-libtorrent-dbg ++ package (Closes: #628041). ++ ++ -- Cristian Greco Mon, 20 Jun 2011 01:30:51 +0200 ++ ++libtorrent-rasterbar (0.15.6-1) unstable; urgency=low ++ ++ * New upstream version. ++ - upload to unstable. ++ * Getting rid of the unneeded *.la file in -dev package ++ (Closes: #622461). ++ * debian/control: bump Standards-Version to 3.9.2. ++ * debian/README.Debian: fix a spelling error in an old entry ++ to make lintian happy. ++ * debian/rules: simplified a bit. ++ * debian/{control,rules}: drop "XS-Python-Version: all" and use ++ different pyversions options instead. ++ ++ -- Cristian Greco Tue, 10 May 2011 20:54:50 +0200 ++ ++libtorrent-rasterbar (0.15.5-1) experimental; urgency=low ++ ++ * New upstream version. ++ ++ -- Cristian Greco Fri, 07 Jan 2011 17:36:29 +0100 ++ ++libtorrent-rasterbar (0.15.4-1) experimental; urgency=low ++ ++ * New upstream version. ++ - upload to experimental: the library is pretty stable, but will be ++ uploaded to unstable only after the release of Squeeze and the ++ update of boost-defaults to boost1.44 (or higher). ++ * debian/control: ++ - new package libtorrent-rasterbar6 (bump up library soname). ++ - rework boost build-deps, drop unneeded. ++ - bump up Standards-Version to 3.9.1. ++ * debian/rules: ++ - rework (update) configuration options. ++ - bump debhelper build-dep to (>= 7.4.10) and enable parallel ++ build support. ++ * debian/copyright: ++ - updated to reflect upstream changes. ++ - fix copyright-refers-to-deprecated-bsd-license-file. ++ * debian/patches/100_fix_html_docs.patch: refreshed. ++ * debian/watch: use a simplified regexp. ++ ++ -- Cristian Greco Fri, 10 Dec 2010 00:18:46 +0100 ++ ++libtorrent-rasterbar (0.14.10-2) unstable; urgency=low ++ ++ * debian/{control,rules}: build python-libtorrent for all supported ++ python versions. (Closes: #578078) ++ * debian/README.source: remove now unneeded file. ++ ++ -- Cristian Greco Sat, 17 Apr 2010 00:19:00 +0200 ++ ++libtorrent-rasterbar (0.14.10-1) unstable; urgency=low ++ ++ * New upstream version. ++ ++ -- Cristian Greco Wed, 24 Mar 2010 18:23:04 +0100 ++ ++libtorrent-rasterbar (0.14.9-1) unstable; urgency=low ++ ++ * New upstream version. ++ * debian/libtorrent-rasterbar-doc.doc-base: fix spelling error. ++ * debian/patches/100_fix_html_docs.patch: refreshed. ++ * debian/control: bump up standards-version to 3.8.4 (no changes). ++ ++ -- Cristian Greco Sat, 20 Feb 2010 02:00:20 +0100 ++ ++libtorrent-rasterbar (0.14.8-1) unstable; urgency=low ++ ++ * New upstream version. ++ - r4072_fix_broadcast_socket_regression.patch: dropped, included upstream. ++ * debian/control: add dependency on libgeoip-dev for -dev package. ++ * debian/watch: point download url to googlecode. ++ ++ -- Cristian Greco Thu, 14 Jan 2010 23:25:42 +0100 ++ ++libtorrent-rasterbar (0.14.7-2) unstable; urgency=low ++ ++ * r4072_fix_broadcast_socket_regression.patch: new patch, pulled from ++ upstream svn (r4072). ++ ++ -- Cristian Greco Thu, 17 Dec 2009 13:21:49 +0100 ++ ++libtorrent-rasterbar (0.14.7-1) unstable; urgency=low ++ ++ * New upstream version. ++ * debian/control: ++ - new maintainer email address. ++ - add DMUA field, thanks to my sponsor Michal Čihař. ++ - add build-dep on libboost-program-options-dev. ++ * Convert to 3.0 (quilt) source format. ++ ++ -- Cristian Greco Wed, 25 Nov 2009 16:29:30 +0100 ++ ++libtorrent-rasterbar (0.14.6-1) unstable; urgency=low ++ ++ * New upstream version. ++ * debian/{control,rules}: ++ - build-dep on libgeoip-dev and pkg-config, thus enable building against ++ geoip system library. (Closes: #544462) ++ - drop build-dep on autotools-dev and don't overwrite config.{guess,sub} ++ as upstream now ships up-to-date files. ++ * debian/rules: drop the hack for boost1.39 as latest revision now ++ ship again the -mt simlinks. ++ * 010_r3796_r3797_fix_configure.patch: deleted (merged upstream). ++ * debian/README.source: fix reference to quilt doc. ++ ++ -- Cristian Greco Tue, 22 Sep 2009 12:58:26 +0200 ++ ++libtorrent-rasterbar (0.14.5-1) unstable; urgency=low ++ ++ * New upstream version. ++ - new package libtorrent-rasterbar5 (bump up library soname). ++ - debian/patches: ++ + prefix patches with a number. ++ + 100_fix_html_docs.patch: improve description. ++ + new patch 010_r3796_r3797_fix_configure.patch (temporary fix). ++ * debian/control: ++ - move git package maintenance to collab-maint. ++ - bump up Standards-Version to 3.8.3 (no changes required). ++ - (build-)depends on zlib1g-dev instead of libz-dev (which is ++ a virtual package). ++ - build-depends on quilt (>= 0.46-7) and simplify debian/rules. ++ * debian/rules: allow building against boost 1.39 (which contains ++ libraries with no more -mt suffix) and try to retain compatibility ++ with older versions for ubuntu backports. ++ ++ -- Cristian Greco Tue, 25 Aug 2009 17:29:29 +0200 ++ ++libtorrent-rasterbar (0.14.4-1) unstable; urgency=low ++ ++ * New upstream version. ++ - new package libtorrent-rasterbar4 (bump up library soname). ++ * debian/control: ++ - fix Vcs-Browser address; ++ - build depends on new unversioned boost -dev packages (this will cause ++ building against boost1.38). (Closes: #531114) ++ * debian/patches/fix_html_docs.patch: refresh patch as file ++ docs/libtorrent_plugins.rst was added upstream. ++ * debian/rules: ++ - now there is no need to remove the missing doc file generated by ++ fix_html_docs.patch as it has been fixed upstream; ++ - backup config.{sub,guess} before overwriting them during build, and ++ restore during clean. ++ ++ -- Cristian Greco Wed, 03 Jun 2009 13:05:21 +0200 ++ ++libtorrent-rasterbar (0.14.3-1) unstable; urgency=low ++ ++ * New Upstream Version ++ - new package libtorrent-rasterbar3 (bump up library soname). ++ * debian/control: ++ - add Vcs-* stuff (switch to git-buildpackage) and bump up ++ Standards-Version to 3.8.1 (no changes required); ++ - build-depends on debhelper (>= 7.0.50) and use override_dh_command; ++ - move -dbg package to the new 'debug' section; ++ - build-depends on autotools-dev and overwrite config.{sub,guess} with a ++ recent version in debian/rules. ++ - build-depends on quilt and python-docutils: ++ + debian/patches/fix_html_docs.patch: fix html documentation for offline ++ browsing and add another missing doc file; ++ + rebuild docs at build time in debian/rules; ++ * debian/example.makefile: install a simple makefile for example programs ++ included in -doc package. ++ * debian/rules, debian/python-libtorrent.install: don't rely on hardcoded ++ python version, use --install-layout=deb when building python bindings, ++ and install to '*-packages' instead of 'site-packages' (this should ++ prepare for python2.6 and minimize Ubuntu diff). ++ ++ -- Cristian Greco Wed, 13 May 2009 12:08:59 +0200 ++ ++libtorrent-rasterbar (0.14.2-2) unstable; urgency=low ++ ++ * debian/control, debian/rules: revert building only against boost1.37 since ++ the dependencies on the -dev packages are causing some problems with ++ reverse depends clients and backporting to stable is not really needed ++ (too much maintenance work) at the moment. ++ * debian/README.source: deleted. ++ ++ -- Cristian Greco Wed, 11 Mar 2009 16:27:20 +0100 ++ ++libtorrent-rasterbar (0.14.2-1) unstable; urgency=low ++ ++ * New upstream version. ++ - new package libtorrent-rasterbar2 (bump up library SONAME); ++ - remove patch 01_fix_ax_python_m4.patch (merged upstream); ++ - debian/control, debian/rules, debian/README.source: drop build ++ dependency on quilt system. ++ * debian/control, debian/rules, debian/README.source: adjusting build ++ dependencies and configure flags to build against boost1.37 and continue ++ to provide compatibility with boost1.34.1 for backports (adding some notes). ++ * debian/control: fix lintian I:binary-control-field-duplicates-source. ++ * Moving section from 'Programming' to 'Programming/C++' for the doc-base ++ files in libtorrent-rasterbar-doc. ++ * Closing a bug already fixed in 0.14-1: fixed documentation about ++ storage_mode_allocate (Closes: #502241). ++ * Upload to unstable (Closes: #517000). ++ ++ -- Cristian Greco Fri, 27 Feb 2009 07:46:38 +0100 ++ ++libtorrent-rasterbar (0.14.1-1) experimental; urgency=low ++ ++ * New upstream version ++ - remove patch 01_python_bindings.patch (included upstream); ++ - add patch 01_fix_ax_python_m4.patch (already merged upstream, see ++ description for details). ++ * Revert build-depends on libboost-dev (>= 1.34.1) and libasio-dev because ++ boost1.36 has been removed from unstable and boost1.37 is not yet fully ++ supported by the library (added notes on README.Debian). ++ * Install .cpp examples using dh_installexamples and a .examples file. ++ * debian/copyright: fix main BSD license text. ++ ++ -- Cristian Greco Mon, 15 Dec 2008 15:34:17 +0100 ++ ++libtorrent-rasterbar (0.14-1) experimental; urgency=low ++ ++ * New upstream version. ++ - debian/copyright: new and updated copyright notes and files. ++ - debian/control: ++ + build-depends on libboost1.36-dev and python-dev; ++ + new package libtorrent-rasterbar1 (bump up library SONAME); ++ + new package python-libtorrent which contains python bindings ++ for the library. ++ - debian/rules: ++ + use multi-threaded boost libraries (--with-boost-system=mt, ...); ++ + add --disable-examples and --disable-tests options; ++ + delete specific information about debian revision in dh_makeshlibs ++ (i.e., use only dh_makeshlibs -V). ++ - debian/control, debian/rules, debian/README.source, debian/patches: ++ build-depends on quilt patch system and install a patch of mine ++ (01_python_bindings, already merged upstream) in order to build python ++ bindings; use a temporary fix for the 'clean' target. ++ * Install example files (.cpp) with the -doc package. ++ * Provide a doc-base control file to install html docs ++ (libtorrent-rasterbar-doc.doc-base). ++ * debian/README.Debian: new notes about the python package and the lintian ++ tag 'no-symbols-control-file' for libtorrent-rasterbar1. ++ * Close a bug in Ubuntu about build with --disable-debug, already solved ++ with 0.13.1-2 (LP: #291448). ++ * Upload to experimental to test the new python bindings package. ++ * New maintainer email address. ++ ++ -- Cristian Greco Sun, 23 Nov 2008 18:55:24 +0100 ++ ++libtorrent-rasterbar (0.13.1-2) unstable; urgency=low ++ ++ * debian/rules: ++ - build with --disable-debug option because enabling debug seems to cause ++ some clients to crash, and most upstream authors prefer a release build of ++ the library. ++ - added specific version information to dh_makeshlibs so that reverse ++ dependencies will depend on this particular version. ++ * README.Debian: updated with info about --disable-debug. ++ ++ -- Cristian Greco Wed, 23 Jul 2008 11:18:31 +0200 ++ ++libtorrent-rasterbar (0.13.1-1) unstable; urgency=low ++ ++ * Initial release (Closes: #402462) ++ ++ -- Cristian Greco Wed, 02 Jul 2008 10:46:21 +0200 diff --cc debian/control index 0000000,0000000..afe7868 new file mode 100644 --- /dev/null +++ b/debian/control @@@ -1,0 -1,0 +1,80 @@@ ++Source: libtorrent-rasterbar ++Section: libs ++Maintainer: Christian Marillat ++Homepage: https://libtorrent.org/ ++Standards-Version: 4.7.4 ++Build-Depends-Indep: python3-docutils, python3-pygments, python3-aafigure, ++ graphviz, imagemagick, python3-sphinxcontrib.svg2pdfconverter, icoutils ++Build-Depends: dpkg-dev (>= 1.22.5), debhelper-compat (= 14), python3-dev, ++ dh-sequence-python3, libssl-dev, pkgconf, cmake, ninja-build, ++ python3-setuptools, libboost-python-dev, ++ ++Package: libtorrent-rasterbar2.0t64 ++Provides: ${t64:Provides} ++Replaces: libtorrent-rasterbar2.0, ++Breaks: libtorrent-rasterbar2.0 (<< ${source:Version}), ++Architecture: any ++Multi-Arch: same ++Depends: ${shlibs:Depends}, ${misc:Depends}, ++Description: C++ bittorrent library by Rasterbar Software ++ Bittorrent library by Rasterbar Software (Arvid Norberg). ++ libtorrent-rasterbar is a C++ library that aims to be a good alternative to ++ all the other bittorrent implementations around. ++ . ++ The main goals of libtorrent-rasterbar are: ++ * to be cpu efficient ++ * to be memory efficient ++ * to be very easy to use ++ . ++ This package contains libtorrent-rasterbar run time shared library. ++ ++Package: libtorrent-rasterbar-dev ++Section: libdevel ++Architecture: any ++Multi-Arch: same ++Depends: ${misc:Depends}, libtorrent-rasterbar2.0t64 (= ${binary:Version}), libssl-dev, pkgconf, libboost-dev, ++Suggests: libtorrent-rasterbar-doc, ++Description: Development files for libtorrent-rasterbar ++ Bittorrent library by Rasterbar Software (Arvid Norberg). ++ libtorrent-rasterbar is a C++ library that aims to be a good alternative to ++ all the other bittorrent implementations around. ++ . ++ The main goals of libtorrent-rasterbar are: ++ * to be cpu efficient ++ * to be memory efficient ++ * to be very easy to use ++ . ++ This package contains header files and static version of the library. ++ ++Package: libtorrent-rasterbar-doc ++Section: doc ++Architecture: all ++Multi-Arch: foreign ++Depends: ${misc:Depends}, ++Description: Documentation for libtorrent-rasterbar ++ Bittorrent library by Rasterbar Software (Arvid Norberg). ++ libtorrent-rasterbar is a C++ library that aims to be a good alternative to ++ all the other bittorrent implementations around. ++ . ++ The main goals of libtorrent-rasterbar are: ++ * to be cpu efficient ++ * to be memory efficient ++ * to be very easy to use ++ . ++ This package contains documentation and example files. ++ ++Package: python3-libtorrent ++Section: python ++Architecture: any ++Depends: ${shlibs:Depends}, ${misc:Depends}, ${python3:Depends}, ++Description: Python bindings for libtorrent-rasterbar (Python 3) ++ Bittorrent library by Rasterbar Software (Arvid Norberg). ++ libtorrent-rasterbar is a C++ library that aims to be a good alternative to ++ all the other bittorrent implementations around. ++ . ++ The main goals of libtorrent-rasterbar are: ++ * to be cpu efficient ++ * to be memory efficient ++ * to be very easy to use ++ . ++ This package contains Python 3 bindings for the libtorrent-rasterbar library. diff --cc debian/copyright index 0000000,0000000..aaa19cc new file mode 100644 --- /dev/null +++ b/debian/copyright @@@ -1,0 -1,0 +1,147 @@@ ++Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ ++Packaged-By: Cristian Greco ++Upstream-Name: libtorrent ++Upstream-Contact: Arvid Norberg ++Source: https://github.com/arvidn/libtorrent/releases ++Files-Excluded: ++ .* ++ ++Files: * ++Copyright: 2003-2026, Arvid Norberg ++ 2014-2026, Alden Torres ++ 2005-2026, Steven Siloti ++ 2003-2026, Daniel Wallin ++ 2006-2026, Magnus Jonsson ++ 2007-2026, Un Shyam ++ 2006-2026, MassaRoddel ++ 2006-2026, Thomas Yuan ++License: BSD-3-clause ++ Redistribution and use in source and binary forms, with or without ++ modification, are permitted provided that the following conditions ++ are met: ++ . ++ * Redistributions of source code must retain the above copyright ++ notice, this list of conditions and the following disclaimer. ++ * Redistributions in binary form must reproduce the above copyright ++ notice, this list of conditions and the following disclaimer in ++ the documentation and/or other materials provided with the ++ distribution. ++ * Neither the name of the author nor the names of its ++ contributors may be used to endorse or promote products derived ++ from this software without specific prior written permission. ++ . ++ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++ POSSIBILITY OF SUCH DAMAGE. ++ ++Files: debian/* ++Copyright: 2021-2026, Christian Marillat ++ 2008, Cristian Greco ++License: GPL-2+ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 2 of the License, or ++ (at your option) any later version. ++ . ++ This program is distributed in the hope that it will be useful, but WITHOUT ++ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ more details. ++Comment: ++ On Debian systems, the full text of the GNU General Public License version 2 ++ can be found in the file "/usr/share/common-licenses/GPL-2". ++ ++Files: bindings/python/* ++Copyright: 2004-2007, Daniel Wallin ++ 2006-2008, Arvid Norberg ++ 2008, Andrew Resch ++ 2015, John Maddock ++License: BSL-1.0 ++ Boost Software License - Version 1.0 - August 17th, 2003 ++ . ++ Permission is hereby granted, free of charge, to any person or organization ++ obtaining a copy of the software and accompanying documentation covered by ++ this license (the "Software") to use, reproduce, display, distribute, ++ execute, and transmit the Software, and to prepare derivative works of the ++ Software, and to permit third-parties to whom the Software is furnished to ++ do so, all subject to the following: ++ . ++ The copyright notices in the Software and this entire statement, including ++ the above license grant, this restriction and the following disclaimer, ++ must be included in all copies of the Software, in whole or in part, and ++ all derivative works of the Software, unless such copies or derivative ++ works are solely in the form of machine-executable object code generated by ++ a source language processor. ++ . ++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT ++ SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE ++ FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ++ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ DEALINGS IN THE SOFTWARE. ++ ++Files: include/libtorrent/puff.hpp ++ src/puff.cpp ++Copyright: 2002-2003, Mark Adler ++License: Zlib ++ This software is provided 'as-is', without any express or implied ++ warranty. In no event will the author be held liable for any damages ++ arising from the use of this software. ++ . ++ Permission is granted to anyone to use this software for any purpose, ++ including commercial applications, and to alter it and redistribute it ++ freely, subject to the following restrictions: ++ . ++ 1. The origin of this software must not be misrepresented; you must not ++ claim that you wrote the original software. If you use this software ++ in a product, an acknowledgment in the product documentation would ++ be appreciated but is not required. ++ 2. Altered source versions must be plainly marked as such, and must not ++ be misrepresented as being the original software. ++ 3. This notice may not be removed or altered from any source ++ distribution. ++ ++Files: src/sha1.cpp ++ include/libtorrent/sha1.hpp ++Copyright: Steve Reid ++Comment: Contributors: ++ Modified 7/98 By James H. Brown ++ Modified 8/98 By Steve Reid ++ Modified 4/01 By Saul Kravitz ++ Converted to C++ 6/04 By Arvid Norberg ++License: public-domain ++ The file sha1.c was put in the public domain by Steve Reid. ++ ++Files: cmake/Modules/ucm_flags.cmake ++Copyright: 2016, Viktor Kirilov ++ 1994, X Consortium ++License: Expat ++ Permission is hereby granted, free of charge, to any person ++ obtaining a copy of this software and associated documentation ++ files (the "Software"), to deal in the Software without ++ restriction, including without limitation the rights to use, ++ copy, modify, merge, publish, distribute, sublicense, and/or sell ++ copies of the Software, and to permit persons to whom the ++ Software is furnished to do so, subject to the following ++ conditions: ++ . ++ The above copyright notice and this permission notice shall be ++ included in all copies or substantial portions of the Software. ++ . ++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES ++ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT ++ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, ++ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ OTHER DEALINGS IN THE SOFTWARE. diff --cc debian/docs index 0000000,0000000..62deb04 new file mode 100644 --- /dev/null +++ b/debian/docs @@@ -1,0 -1,0 +1,1 @@@ ++AUTHORS diff --cc debian/examples.makefile index 0000000,0000000..3722d53 new file mode 100644 --- /dev/null +++ b/debian/examples.makefile @@@ -1,0 -1,0 +1,50 @@@ ++#!/usr/bin/make -f ++# ++# Simple makefile for libtorrent-rasterbar's examples. ++# Copyright © 2009 Cristian Greco ++# This file is released in the public domain. ++# ++# Please note that you need to install the following packages in order to build ++# these example programs: ++# - libtorrent-rasterbar-dev ++# - libboost-program-options*-dev ++# - libboost-regex*-dev ++# (where the `*' means the same version of boost development packages which ++# libtorrent-rasterbar-dev actually depends on). ++ ++CXX = g++ ++ ++CXXFLAGS = -ftemplate-depth-50 -DBOOST_MULTI_INDEX_DISABLE_SERIALIZATION ++ ++TORRENT_CFLAGS = $(shell pkg-config libtorrent-rasterbar --cflags) ++TORRENT_LIBS = $(shell pkg-config libtorrent-rasterbar --libs) ++ ++BOOST_PROGRAM_OPTIONS_LIBS = -lboost_program_options-mt ++BOOST_REGEX_LIBS = -lboost_regex-mt ++ ++examples_BIN = client_test dump_torrent make_torrent simple_client enum_if ++ ++all: $(examples_BIN) ++ ++client_test: client_test.cpp ++ @rm -f client_test ++ $(CXX) $(CXXFLAGS) $(TORRENT_CFLAGS) -o $@ $< $(TORRENT_LIBS) $(BOOST_PROGRAM_OPTIONS_LIBS) $(BOOST_REGEX_LIBS) ++ ++dump_torrent: dump_torrent.cpp ++ @rm -f dump_torrent ++ $(CXX) $(CXXFLAGS) $(TORRENT_CFLAGS) -o $@ $< $(TORRENT_LIBS) ++ ++make_torrent: make_torrent.cpp ++ @rm -f make_torrent ++ $(CXX) $(CXXFLAGS) $(TORRENT_CFLAGS) -o $@ $< $(TORRENT_LIBS) ++ ++simple_client: simple_client.cpp ++ @rm -f simple_client ++ $(CXX) $(CXXFLAGS) $(TORRENT_CFLAGS) -o $@ $< $(TORRENT_LIBS) ++ ++enum_if: enum_if.cpp ++ @rm -f enum_if ++ $(CXX) $(CXXFLAGS) $(TORRENT_CFLAGS) -o $@ $< $(TORRENT_LIBS) ++ ++clean: ++ @rm -f $(examples_BIN) diff --cc debian/libtorrent-rasterbar-dev.install index 0000000,0000000..7b90b0c new file mode 100644 --- /dev/null +++ b/debian/libtorrent-rasterbar-dev.install @@@ -1,0 -1,0 +1,5 @@@ ++usr/include ++usr/lib/*/lib*.so ++usr/lib/*/pkgconfig ++usr/lib/*/cmake ++usr/share/cmake diff --cc debian/libtorrent-rasterbar-doc.doc-base index 0000000,0000000..6c8e9f1 new file mode 100644 --- /dev/null +++ b/debian/libtorrent-rasterbar-doc.doc-base @@@ -1,0 -1,0 +1,11 @@@ ++Document: libtorrent-rasterbar ++Section: Programming/C++ ++Title: Libtorrent-rasterbar Documentation ++Author: Arvid Norberg ++Abstract: This documentation describes mainly the API ++ interface of libtorrent-rasterbar and contains other ++ useful information for developers. ++ ++Format: HTML ++Index: /usr/share/doc/libtorrent-rasterbar-doc/html/index.html ++Files: /usr/share/doc/libtorrent-rasterbar-doc/html/*.html diff --cc debian/libtorrent-rasterbar-doc.examples index 0000000,0000000..8422eb5 new file mode 100644 --- /dev/null +++ b/debian/libtorrent-rasterbar-doc.examples @@@ -1,0 -1,0 +1,2 @@@ ++examples/*.cpp ++debian/examples.makefile diff --cc debian/libtorrent-rasterbar-doc.install index 0000000,0000000..b425fc2 new file mode 100644 --- /dev/null +++ b/debian/libtorrent-rasterbar-doc.install @@@ -1,0 -1,0 +1,3 @@@ ++docs/*.html usr/share/doc/libtorrent-rasterbar-doc/html/ ++docs/style.css usr/share/doc/libtorrent-rasterbar-doc/html/ ++docs/img/* usr/share/doc/libtorrent-rasterbar-doc/html/img/ diff --cc debian/libtorrent-rasterbar2.0.install index 0000000,0000000..3ddde58 new file mode 100644 --- /dev/null +++ b/debian/libtorrent-rasterbar2.0.install @@@ -1,0 -1,0 +1,1 @@@ ++usr/lib/*/lib*.so.* diff --cc debian/libtorrent-rasterbar2.0t64.install index 0000000,0000000..3ddde58 new file mode 100644 --- /dev/null +++ b/debian/libtorrent-rasterbar2.0t64.install @@@ -1,0 -1,0 +1,1 @@@ ++usr/lib/*/lib*.so.* diff --cc debian/libtorrent-rasterbar2.0t64.lintian-overrides index 0000000,0000000..43558c6 new file mode 100644 --- /dev/null +++ b/debian/libtorrent-rasterbar2.0t64.lintian-overrides @@@ -1,0 -1,0 +1,1 @@@ ++libtorrent-rasterbar2.0t64: package-name-doesnt-match-sonames libtorrent-rasterbar2.0 diff --cc debian/patches/01_git-submodules.patch index 0000000,0000000..3fd49cb new file mode 100644 --- /dev/null +++ b/debian/patches/01_git-submodules.patch @@@ -1,0 -1,0 +1,37734 @@@ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/deps/try_signal/.travis.yml ++@@ -0,0 +1,44 @@ +++language: cpp +++matrix: +++ include: +++ - env: toolset=gcc +++ - os: osx +++ osx_image: xcode11.2 +++ env: toolset=darwin +++ +++branches: +++ only: +++ - master +++ +++git: +++ submodules: false +++ depth: 1 +++ +++addons: +++ apt: +++ sources: +++ - ubuntu-toolchain-r-test +++ packages: +++ - libboost-tools-dev +++ - g++-9 +++ +++install: +++ +++ - 'if [[ $TRAVIS_OS_NAME == "osx" ]]; then brew update > /dev/null && brew install boost-build; fi' +++ - 'if [[ $TRAVIS_OS_NAME != "osx" ]]; then +++ export B2=bjam; +++ else +++ export B2=b2; +++ fi' +++ - touch ~/user-config.jam +++ - 'if [[ $toolset == "gcc" ]]; then +++ g++-5 --version; +++ echo "using gcc : : g++-5 : -std=c++11 ;" >> ~/user-config.jam; +++ fi' +++ - 'echo "using darwin : : clang++ : -std=c++11 ;" >> ~/user-config.jam' +++ +++script: +++ +++ - ${B2} link=static stage_test +++ - ./test +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/deps/try_signal/CMakeLists.txt ++@@ -0,0 +1,6 @@ +++cmake_minimum_required(VERSION 2.8.12) +++project(try_signal) +++ +++add_library(try_signal signal_error_code try_signal) +++target_include_directories(try_signal PUBLIC .) +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/deps/try_signal/Jamfile ++@@ -0,0 +1,18 @@ +++lib try_signal +++ : # sources +++ signal_error_code.cpp try_signal.cpp +++ : # requirements +++ : # default build +++ static +++ : # usage requirements +++ . +++ ; +++ +++exe test : test.cpp : try_signal static ; +++explicit test ; +++ +++exe example : example.cpp : try_signal static ; +++explicit example ; +++ +++install stage_test : test : . ; +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/deps/try_signal/LICENSE ++@@ -0,0 +1,29 @@ +++BSD 3-Clause License +++ +++Copyright (c) 2016, Arvid Norberg +++All rights reserved. +++ +++Redistribution and use in source and binary forms, with or without +++modification, are permitted provided that the following conditions are met: +++ +++* Redistributions of source code must retain the above copyright notice, this +++ list of conditions and the following disclaimer. +++ +++* Redistributions in binary form must reproduce the above copyright notice, +++ this list of conditions and the following disclaimer in the documentation +++ and/or other materials provided with the distribution. +++ +++* Neither the name of the copyright holder nor the names of its +++ contributors may be used to endorse or promote products derived from +++ this software without specific prior written permission. +++ +++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +++AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +++IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +++DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +++FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +++DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +++SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +++CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +++OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +++OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/deps/try_signal/README.rst ++@@ -0,0 +1,53 @@ +++try_signal +++========== +++ +++.. image:: https://travis-ci.org/arvidn/try_signal.svg?branch=master +++ :target: https://travis-ci.org/arvidn/try_signal +++ +++.. image:: https://ci.appveyor.com/api/projects/status/le8jjroaai8081f1?svg=true +++ :target: https://ci.appveyor.com/project/arvidn/try-signal/branch/master +++ +++The ``try_signal`` library provide a way to turn signals into C++ exceptions. +++This is especially useful when performing disk I/O via memory mapped files, +++where I/O errors are reported as ``SIGBUS`` and ``SIGSEGV`` or as structured +++exceptions on windows. +++ +++The function ``try_signal`` takes a function object that will be executed once. +++If the function causes a signal (or structured exception) to be raised, it will +++throw a C++ exception. Note that RAII may not be relied upon within this function. +++It may not rely on destructors being called. Stick to simple operations like +++memcopy. +++ +++Example:: +++ +++ #include +++ #include +++ #include +++ #include "try_signal.hpp" +++ #include +++ #include +++ #include +++ +++ int main() try +++ { +++ int fd = open("test_file", O_RDWR); +++ void* map = mmap(nullptr, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); +++ +++ std::vector buf(1024); +++ std::iota(buf.begin(), buf.end(), 0); +++ +++ // disk full or access after EOF are reported as exceptions +++ sig::try_signal([&]{ +++ std::memcpy(map, buf.data(), buf.size()); +++ }); +++ +++ munmap(map, 1024); +++ close(fd); +++ return 0; +++ } +++ catch (std::exception const& e) +++ { +++ fprintf(stderr, "exited with exception: %s\n", e.what()); +++ return 1; +++ } +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/deps/try_signal/appveyor.yml ++@@ -0,0 +1,50 @@ +++version: "{build}" +++branches: +++ only: +++ - master +++os: Visual Studio 2015 +++clone_depth: 1 +++environment: +++ matrix: +++ - variant: debug +++ compiler: msvc-14.0 +++ model: 64 +++ - variant: debug +++ compiler: msvc-14.0 +++ model: 32 +++ - variant: release +++ compiler: msvc-14.0 +++ model: 64 +++ - variant: debug +++ compiler: gcc +++ model: 32 +++ - variant: debug +++ compiler: gcc +++ model: 64 +++ - variant: release +++ compiler: gcc +++ model: 32 +++ +++install: +++- set ROOT_DIRECTORY=%CD% +++- set BOOST_ROOT=c:\Libraries\boost_1_67_0 +++- set BOOST_BUILD_PATH=%BOOST_ROOT%\tools\build +++- echo %BOOST_ROOT% +++- echo %BOOST_BUILD_PATH% +++- set PATH=%PATH%;%BOOST_BUILD_PATH%\src\engine\bin.ntx86 +++- ps: '"using msvc : 14.0 ;`nusing gcc : : : -std=c++11 ;" | Set-Content $env:HOMEDRIVE\$env:HOMEPATH\user-config.jam' +++- type %HOMEDRIVE%%HOMEPATH%\user-config.jam +++- set PATH=c:\msys64\mingw32\bin;%PATH% +++- g++ --version +++- python --version +++- echo %ROOT_DIRECTORY% +++- cd %BOOST_BUILD_PATH%\src\engine +++- build.bat >nul +++- cd %ROOT_DIRECTORY% +++ +++build_script: +++# examples +++- b2.exe warnings=all warnings-as-errors=on -j2 %compiler% address-model=%model% variant=%variant% stage_test +++ +++test_script: +++- test ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/deps/try_signal/example.cpp ++@@ -0,0 +1,31 @@ +++#include +++#include +++#include +++#include "try_signal.hpp" +++#include +++#include +++#include +++ +++int main() try +++{ +++ int fd = open("test_file", O_RDWR); +++ void* map = mmap(nullptr, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); +++ +++ std::vector buf(1024); +++ std::iota(buf.begin(), buf.end(), 0); +++ +++ // disk full or access after EOF are reported as exceptions +++ sig::try_signal([&]{ +++ std::memcpy(map, buf.data(), buf.size()); +++ }); +++ +++ munmap(map, 1024); +++ close(fd); +++ return 0; +++} +++catch (std::exception const& e) +++{ +++ fprintf(stderr, "exited with exception: %s\n", e.what()); +++ return 1; +++} +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/deps/try_signal/signal_error_code.cpp ++@@ -0,0 +1,209 @@ +++/* +++ +++Copyright (c) 2016, Arvid Norberg +++All rights reserved. +++ +++Redistribution and use in source and binary forms, with or without +++modification, are permitted provided that the following conditions +++are met: +++ +++ * Redistributions of source code must retain the above copyright +++ notice, this list of conditions and the following disclaimer. +++ * Redistributions in binary form must reproduce the above copyright +++ notice, this list of conditions and the following disclaimer in +++ the documentation and/or other materials provided with the distribution. +++ * Neither the name of the author nor the names of its +++ contributors may be used to endorse or promote products derived +++ from this software without specific prior written permission. +++ +++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +++AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +++IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +++ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +++LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +++CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +++SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +++INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +++CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +++ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +++POSSIBILITY OF SUCH DAMAGE. +++ +++*/ +++ +++#include +++#include +++ +++#include "signal_error_code.hpp" +++ +++namespace { +++ +++ struct signal_error_category : std::error_category +++ { +++ const char* name() const noexcept override +++ { return "signal"; } +++ std::string message(int ev) const noexcept override +++ { +++#define SIGNAL_CASE(x) case sig::errors::error_code_enum:: x: return #x; +++ switch (ev) +++ { +++ SIGNAL_CASE(abort) +++ SIGNAL_CASE(alarm) +++ SIGNAL_CASE(arithmetic_exception) +++ SIGNAL_CASE(hangup) +++ SIGNAL_CASE(illegal) +++ SIGNAL_CASE(interrupt) +++ SIGNAL_CASE(kill) +++ SIGNAL_CASE(pipe) +++ SIGNAL_CASE(quit) +++ case sig::errors::error_code_enum::segmentation: return "segmentation fault"; +++ SIGNAL_CASE(terminate) +++ SIGNAL_CASE(user1) +++ SIGNAL_CASE(user2) +++ SIGNAL_CASE(child) +++ SIGNAL_CASE(cont) +++ SIGNAL_CASE(stop) +++ SIGNAL_CASE(terminal_stop) +++ SIGNAL_CASE(terminal_in) +++ SIGNAL_CASE(terminal_out) +++ SIGNAL_CASE(bus) +++#ifdef SIGPOLL +++ SIGNAL_CASE(poll) +++#endif +++ SIGNAL_CASE(profiler) +++ SIGNAL_CASE(system_call) +++ SIGNAL_CASE(trap) +++ SIGNAL_CASE(urgent_data) +++ SIGNAL_CASE(virtual_timer) +++ SIGNAL_CASE(cpu_limit) +++ SIGNAL_CASE(file_size_limit) +++ default: return "unknown"; +++ } +++#undef SIGNAL_CASE +++ } +++ std::error_condition default_error_condition(int ev) const noexcept override +++ { return {ev, *this}; } +++ }; +++} // anonymous namespace +++ +++namespace sig { +++namespace errors { +++ +++ std::error_code make_error_code(error_code_enum e) +++ { +++ return {e, sig_category()}; +++ } +++ +++ std::error_condition make_error_condition(error_code_enum e) +++ { +++ return {e, sig_category()}; +++ } +++ +++} // namespace errors +++ +++std::error_category& sig_category() +++{ +++ static signal_error_category signal_category; +++ return signal_category; +++} +++ +++#ifdef _WIN32 +++ +++namespace { +++ sig::errors::error_code_enum map_exception_code(int const ev) +++ { +++ switch (ev) +++ { +++ case seh_errors::error_code_enum::access_violation: +++ case seh_errors::error_code_enum::array_bounds_exceeded: +++ case seh_errors::error_code_enum::guard_page: +++ case seh_errors::error_code_enum::stack_overflow: +++ case seh_errors::error_code_enum::flt_stack_check: +++ case seh_errors::error_code_enum::in_page_error: +++ return sig::errors::segmentation; +++ case seh_errors::error_code_enum::breakpoint: +++ case seh_errors::error_code_enum::single_step: +++ return sig::errors::trap; +++ case seh_errors::error_code_enum::datatype_misalignment: +++ return sig::errors::bus; +++ case seh_errors::error_code_enum::flt_denormal_operand: +++ case seh_errors::error_code_enum::flt_divide_by_zero: +++ case seh_errors::error_code_enum::flt_inexact_result: +++ case seh_errors::error_code_enum::flt_invalid_operation: +++ case seh_errors::error_code_enum::flt_overflow: +++ case seh_errors::error_code_enum::flt_underflow: +++ case seh_errors::error_code_enum::int_divide_by_zero: +++ case seh_errors::error_code_enum::int_overflow: +++ return sig::errors::arithmetic_exception; +++ case seh_errors::error_code_enum::illegal_instruction: +++ case seh_errors::error_code_enum::invalid_disposition: +++ case seh_errors::error_code_enum::priv_instruction: +++ case seh_errors::error_code_enum::noncontinuable_exception: +++ case seh_errors::error_code_enum::status_unwind_consolidate: +++ return sig::errors::illegal; +++ case seh_errors::error_code_enum::invalid_handle: +++ return sig::errors::pipe; +++ default: +++ return sig::errors::illegal; +++ } +++ } +++ +++ struct seh_error_category : std::error_category +++ { +++ const char* name() const noexcept override +++ { return "SEH"; } +++ std::string message(int ev) const noexcept override +++ { +++#define SIGNAL_CASE(x) case sig::seh_errors::error_code_enum:: x: return #x; +++ switch (ev) +++ { +++ SIGNAL_CASE(access_violation) +++ SIGNAL_CASE(array_bounds_exceeded) +++ SIGNAL_CASE(guard_page) +++ SIGNAL_CASE(stack_overflow) +++ SIGNAL_CASE(flt_stack_check) +++ SIGNAL_CASE(in_page_error) +++ SIGNAL_CASE(breakpoint) +++ SIGNAL_CASE(single_step) +++ SIGNAL_CASE(datatype_misalignment) +++ SIGNAL_CASE(flt_denormal_operand) +++ SIGNAL_CASE(flt_divide_by_zero) +++ SIGNAL_CASE(flt_inexact_result) +++ SIGNAL_CASE(flt_invalid_operation) +++ SIGNAL_CASE(flt_overflow) +++ SIGNAL_CASE(flt_underflow) +++ SIGNAL_CASE(int_divide_by_zero) +++ SIGNAL_CASE(int_overflow) +++ SIGNAL_CASE(illegal_instruction) +++ SIGNAL_CASE(invalid_disposition) +++ SIGNAL_CASE(priv_instruction) +++ SIGNAL_CASE(noncontinuable_exception) +++ SIGNAL_CASE(status_unwind_consolidate) +++ SIGNAL_CASE(invalid_handle) +++ default: return "unknown"; +++ } +++#undef SIGNAL_CASE +++ } +++ std::error_condition default_error_condition(int ev) const noexcept override +++ { return std::error_condition(map_exception_code(ev), sig_category()); } +++ }; +++} // anonymous namespace +++ +++namespace seh_errors { +++ +++ std::error_code make_error_code(error_code_enum e) +++ { +++ return {static_cast(e), seh_category()}; +++ } +++ +++} // namespace errors +++ +++std::error_category& seh_category() +++{ +++ static seh_error_category seh_category; +++ return seh_category; +++} +++ +++#endif +++ +++} // namespace sig +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/deps/try_signal/signal_error_code.hpp ++@@ -0,0 +1,162 @@ +++/* +++ +++Copyright (c) 2016, Arvid Norberg +++All rights reserved. +++ +++Redistribution and use in source and binary forms, with or without +++modification, are permitted provided that the following conditions +++are met: +++ +++ * Redistributions of source code must retain the above copyright +++ notice, this list of conditions and the following disclaimer. +++ * Redistributions in binary form must reproduce the above copyright +++ notice, this list of conditions and the following disclaimer in +++ the documentation and/or other materials provided with the distribution. +++ * Neither the name of the author nor the names of its +++ contributors may be used to endorse or promote products derived +++ from this software without specific prior written permission. +++ +++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +++AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +++IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +++ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +++LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +++CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +++SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +++INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +++CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +++ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +++POSSIBILITY OF SUCH DAMAGE. +++ +++*/ +++ +++#ifndef SIGNAL_ERROR_CODE_HPP_INCLUDED +++#define SIGNAL_ERROR_CODE_HPP_INCLUDED +++ +++#include +++#include +++ +++#ifdef _WIN32 +++#ifndef WIN32_LEAN_AND_MEAN +++#define WIN32_LEAN_AND_MEAN +++#endif +++#include +++ +++#ifdef __GNUC__ +++#include +++#else +++#include +++#endif +++#endif +++ +++namespace sig { +++namespace errors { +++ +++#ifdef _WIN32 +++#define SIG_ENUM(name, sig) name, +++#else +++#define SIG_ENUM(name, sig) name = sig, +++#endif +++ +++ enum error_code_enum: int +++ { +++ SIG_ENUM(abort, SIGABRT) +++ SIG_ENUM(alarm, SIGALRM) +++ SIG_ENUM(arithmetic_exception, SIGFPE) +++ SIG_ENUM(hangup, SIGHUP) +++ SIG_ENUM(illegal, SIGILL) +++ SIG_ENUM(interrupt, SIGINT) +++ SIG_ENUM(kill, SIGKILL) +++ SIG_ENUM(pipe, SIGPIPE) +++ SIG_ENUM(quit, SIGQUIT) +++ SIG_ENUM(segmentation, SIGSEGV) +++ SIG_ENUM(terminate, SIGTERM) +++ SIG_ENUM(user1, SIGUSR1) +++ SIG_ENUM(user2, SIGUSR2) +++ SIG_ENUM(child, SIGCHLD) +++ SIG_ENUM(cont, SIGCONT) +++ SIG_ENUM(stop, SIGSTOP) +++ SIG_ENUM(terminal_stop, SIGTSTP) +++ SIG_ENUM(terminal_in, SIGTTIN) +++ SIG_ENUM(terminal_out, SIGTTOU) +++ SIG_ENUM(bus, SIGBUS) +++#ifdef SIGPOLL +++ SIG_ENUM(poll, SIGPOLL) +++#endif +++ SIG_ENUM(profiler, SIGPROF) +++ SIG_ENUM(system_call, SIGSYS) +++ SIG_ENUM(trap, SIGTRAP) +++ SIG_ENUM(urgent_data, SIGURG) +++ SIG_ENUM(virtual_timer, SIGVTALRM) +++ SIG_ENUM(cpu_limit, SIGXCPU) +++ SIG_ENUM(file_size_limit, SIGXFSZ) +++ }; +++ +++#undef SIG_ENUM +++ +++ std::error_code make_error_code(error_code_enum e); +++ std::error_condition make_error_condition(error_code_enum e); +++ +++} // namespace errors +++ +++std::error_category& sig_category(); +++ +++#ifdef _WIN32 +++namespace seh_errors { +++ +++ // standard error codes are "int", the win32 exceptions are DWORD (i.e. +++ // unsigned int). We coerce them into int here for compatibility, and we're +++ // not concerned about their arithmetic +++ enum error_code_enum: int +++ { +++ access_violation = int(EXCEPTION_ACCESS_VIOLATION), +++ array_bounds_exceeded = int(EXCEPTION_ARRAY_BOUNDS_EXCEEDED), +++ guard_page = int(EXCEPTION_GUARD_PAGE), +++ stack_overflow = int(EXCEPTION_STACK_OVERFLOW), +++ flt_stack_check = int(EXCEPTION_FLT_STACK_CHECK), +++ in_page_error = int(EXCEPTION_IN_PAGE_ERROR), +++ breakpoint = int(EXCEPTION_BREAKPOINT), +++ single_step = int(EXCEPTION_SINGLE_STEP), +++ datatype_misalignment = int(EXCEPTION_DATATYPE_MISALIGNMENT), +++ flt_denormal_operand = int(EXCEPTION_FLT_DENORMAL_OPERAND), +++ flt_divide_by_zero = int(EXCEPTION_FLT_DIVIDE_BY_ZERO), +++ flt_inexact_result = int(EXCEPTION_FLT_INEXACT_RESULT), +++ flt_invalid_operation = int(EXCEPTION_FLT_INVALID_OPERATION), +++ flt_overflow = int(EXCEPTION_FLT_OVERFLOW), +++ flt_underflow = int(EXCEPTION_FLT_UNDERFLOW), +++ int_divide_by_zero = int(EXCEPTION_INT_DIVIDE_BY_ZERO), +++ int_overflow = int(EXCEPTION_INT_OVERFLOW), +++ illegal_instruction = int(EXCEPTION_ILLEGAL_INSTRUCTION), +++ invalid_disposition = int(EXCEPTION_INVALID_DISPOSITION), +++ priv_instruction = int(EXCEPTION_PRIV_INSTRUCTION), +++ noncontinuable_exception = int(EXCEPTION_NONCONTINUABLE_EXCEPTION), +++ status_unwind_consolidate = int(STATUS_UNWIND_CONSOLIDATE), +++ invalid_handle = int(EXCEPTION_INVALID_HANDLE), +++ }; +++ +++ std::error_code make_error_code(error_code_enum e); +++} +++ +++std::error_category& seh_category(); +++ +++#endif // _WIN32 +++ +++} // namespace sig +++ +++namespace std +++{ +++template<> +++struct is_error_code_enum : std::true_type {}; +++ +++template<> +++struct is_error_condition_enum : std::true_type {}; +++ +++#ifdef _WIN32 +++template<> +++struct is_error_code_enum : std::true_type {}; +++#endif +++ +++} // namespace std +++ +++#endif +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/deps/try_signal/test.cpp ++@@ -0,0 +1,47 @@ +++#include +++#include +++#include // for memcpy +++ +++#include "try_signal.hpp" +++ +++int main() +++{ +++ char const buf[] = "test...test"; +++ char dest[sizeof(buf)]; +++ +++ { +++ sig::try_signal([&]{ +++ std::memcpy(dest, buf, sizeof(buf)); +++ }); +++ if (!std::equal(buf, buf + sizeof(buf), dest)) { +++ fprintf(stderr, "ERROR: buffer not copied correctly\n"); +++ return 1; +++ } +++ } +++ +++ try { +++ void* invalid_pointer = nullptr; +++ sig::try_signal([&]{ +++ std::memcpy(dest, buf, sizeof(buf)); +++ std::memcpy(dest, invalid_pointer, sizeof(buf)); +++ }); +++ } +++ catch (std::system_error const& e) +++ { +++ if (e.code() != std::error_condition(sig::errors::segmentation)) { +++ fprintf(stderr, "ERROR: expected segmentaiton violation error\n"); +++ } +++ else { +++ fprintf(stderr, "OK\n"); +++ } +++ fprintf(stderr, "exited with expected system_error exception: %s\n", e.what()); +++ +++ // we expect this to happen, so return 0 +++ return e.code() == std::error_condition(sig::errors::segmentation) ? 0 : 1; +++ } +++ +++ // return non-zero here because we don't expect this +++ fprintf(stderr, "ERROR: expected exit through exception\n"); +++ return 1; +++} +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/deps/try_signal/try_signal.cpp ++@@ -0,0 +1,144 @@ +++/* +++ +++Copyright (c) 2016, Arvid Norberg +++All rights reserved. +++ +++Redistribution and use in source and binary forms, with or without +++modification, are permitted provided that the following conditions +++are met: +++ +++ * Redistributions of source code must retain the above copyright +++ notice, this list of conditions and the following disclaimer. +++ * Redistributions in binary form must reproduce the above copyright +++ notice, this list of conditions and the following disclaimer in +++ the documentation and/or other materials provided with the distribution. +++ * Neither the name of the author nor the names of its +++ contributors may be used to endorse or promote products derived +++ from this software without specific prior written permission. +++ +++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +++AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +++IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +++ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +++LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +++CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +++SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +++INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +++CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +++ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +++POSSIBILITY OF SUCH DAMAGE. +++ +++*/ +++ +++#include +++#include +++#include +++#include +++#include +++ +++#include "try_signal.hpp" +++ +++#if !defined _WIN32 +++// linux +++ +++namespace sig { +++namespace detail { +++ +++namespace { +++thread_local sigjmp_buf* jmpbuf = nullptr; +++} +++ +++std::atomic_flag once = ATOMIC_FLAG_INIT; +++ +++scoped_jmpbuf::scoped_jmpbuf(sigjmp_buf* ptr) +++{ +++ _previous_ptr = jmpbuf; +++ jmpbuf = ptr; +++ std::atomic_signal_fence(std::memory_order_release); +++} +++ +++scoped_jmpbuf::~scoped_jmpbuf() { jmpbuf = _previous_ptr; } +++ +++void handler(int const signo, siginfo_t*, void*) +++{ +++ std::atomic_signal_fence(std::memory_order_acquire); +++ if (jmpbuf) +++ siglongjmp(*jmpbuf, signo); +++ +++ // this signal was not caused within the scope of a try_signal object, +++ // invoke the default handler +++ signal(signo, SIG_DFL); +++ raise(signo); +++} +++ +++void setup_handler() +++{ +++ struct sigaction sa; +++ sa.sa_sigaction = &sig::detail::handler; +++ sigemptyset(&sa.sa_mask); +++ sa.sa_flags = SA_SIGINFO; +++ sigaction(SIGSEGV, &sa, nullptr); +++ sigaction(SIGBUS, &sa, nullptr); +++} +++ +++} // detail namespace +++} // sig namespace +++ +++#elif __GNUC__ +++// mingw +++ +++#ifndef WIN32_LEAN_AND_MEAN +++#define WIN32_LEAN_AND_MEAN +++#endif +++#include +++ +++namespace sig { +++namespace detail { +++ +++thread_local jmp_buf* jmpbuf = nullptr; +++ +++long CALLBACK handler(EXCEPTION_POINTERS* pointers) +++{ +++ std::atomic_signal_fence(std::memory_order_acquire); +++ if (jmpbuf) +++ longjmp(*jmpbuf, pointers->ExceptionRecord->ExceptionCode); +++ return EXCEPTION_CONTINUE_SEARCH; +++} +++ +++scoped_handler::scoped_handler(jmp_buf* ptr) +++{ +++ _previous_ptr = jmpbuf; +++ jmpbuf = ptr; +++ std::atomic_signal_fence(std::memory_order_release); +++ _handle = AddVectoredExceptionHandler(1, sig::detail::handler); +++} +++scoped_handler::~scoped_handler() +++{ +++ RemoveVectoredExceptionHandler(_handle); +++ jmpbuf = _previous_ptr; +++} +++ +++} // detail namespace +++} // sig namespace +++ +++#else +++// windows +++ +++#include // for EXCEPTION_* +++ +++namespace sig { +++namespace detail { +++ +++ // these are the kinds of SEH exceptions we'll translate into C++ exceptions +++ bool catch_error(int const code) +++ { +++ return code == EXCEPTION_IN_PAGE_ERROR +++ || code == EXCEPTION_ACCESS_VIOLATION +++ || code == EXCEPTION_ARRAY_BOUNDS_EXCEEDED; +++ } +++} // detail namespace +++} // namespace sig +++ +++#endif // _WIN32 +++ +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/deps/try_signal/try_signal.hpp ++@@ -0,0 +1,49 @@ +++/* +++ +++Copyright (c) 2017, Arvid Norberg +++All rights reserved. +++ +++Redistribution and use in source and binary forms, with or without +++modification, are permitted provided that the following conditions +++are met: +++ +++ * Redistributions of source code must retain the above copyright +++ notice, this list of conditions and the following disclaimer. +++ * Redistributions in binary form must reproduce the above copyright +++ notice, this list of conditions and the following disclaimer in +++ the documentation and/or other materials provided with the distribution. +++ * Neither the name of the author nor the names of its +++ contributors may be used to endorse or promote products derived +++ from this software without specific prior written permission. +++ +++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +++AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +++IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +++ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +++LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +++CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +++SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +++INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +++CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +++ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +++POSSIBILITY OF SUCH DAMAGE. +++ +++*/ +++ +++#ifndef TRY_SIGNAL_HPP_INCLUDED +++#define TRY_SIGNAL_HPP_INCLUDED +++ +++#if !defined _WIN32 +++// linux +++#include "try_signal_posix.hpp" +++#elif __GNUC__ +++// mingw +++#include "try_signal_mingw.hpp" +++#else +++// windows +++#include "try_signal_msvc.hpp" +++#endif +++ +++ +++#endif // TRY_SIGNAL_HPP_INCLUDED +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/deps/try_signal/try_signal_mingw.hpp ++@@ -0,0 +1,78 @@ +++/* +++ +++Copyright (c) 2017, Arvid Norberg +++All rights reserved. +++ +++Redistribution and use in source and binary forms, with or without +++modification, are permitted provided that the following conditions +++are met: +++ +++ * Redistributions of source code must retain the above copyright +++ notice, this list of conditions and the following disclaimer. +++ * Redistributions in binary form must reproduce the above copyright +++ notice, this list of conditions and the following disclaimer in +++ the documentation and/or other materials provided with the distribution. +++ * Neither the name of the author nor the names of its +++ contributors may be used to endorse or promote products derived +++ from this software without specific prior written permission. +++ +++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +++AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +++IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +++ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +++LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +++CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +++SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +++INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +++CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +++ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +++POSSIBILITY OF SUCH DAMAGE. +++ +++*/ +++ +++#ifndef TRY_SIGNAL_MINGW_HPP_INCLUDED +++#define TRY_SIGNAL_MINGW_HPP_INCLUDED +++ +++#include "signal_error_code.hpp" +++ +++#include // for jmp_buf +++ +++#ifndef WIN32_LEAN_AND_MEAN +++#define WIN32_LEAN_AND_MEAN +++#endif +++#include +++ +++namespace sig { +++namespace detail { +++ +++struct scoped_handler +++{ +++ scoped_handler(jmp_buf* ptr); +++ ~scoped_handler(); +++ scoped_handler(scoped_handler const&) = delete; +++ scoped_handler& operator=(scoped_handler const&) = delete; +++private: +++ void* _handle; +++ jmp_buf* _previous_ptr; +++}; +++ +++} // detail namespace +++ +++template +++void try_signal(Fun&& f) +++{ +++ jmp_buf buf; +++ int const code = setjmp(buf); +++ // set the thread local jmpbuf pointer, and make sure it's cleared when we +++ // leave the scope +++ sig::detail::scoped_handler scope(&buf); +++ if (code != 0) +++ throw std::system_error(std::error_code(code, seh_category())); +++ +++ f(); +++} +++ +++} // sig namespace +++ +++#endif +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/deps/try_signal/try_signal_msvc.hpp ++@@ -0,0 +1,61 @@ +++/* +++ +++Copyright (c) 2017, Arvid Norberg +++All rights reserved. +++ +++Redistribution and use in source and binary forms, with or without +++modification, are permitted provided that the following conditions +++are met: +++ +++ * Redistributions of source code must retain the above copyright +++ notice, this list of conditions and the following disclaimer. +++ * Redistributions in binary form must reproduce the above copyright +++ notice, this list of conditions and the following disclaimer in +++ the documentation and/or other materials provided with the distribution. +++ * Neither the name of the author nor the names of its +++ contributors may be used to endorse or promote products derived +++ from this software without specific prior written permission. +++ +++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +++AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +++IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +++ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +++LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +++CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +++SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +++INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +++CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +++ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +++POSSIBILITY OF SUCH DAMAGE. +++ +++*/ +++ +++#ifndef TRY_SIGNAL_MSVC_HPP_INCLUDED +++#define TRY_SIGNAL_MSVC_HPP_INCLUDED +++ +++#include "signal_error_code.hpp" +++ +++namespace sig { +++namespace detail { +++ +++bool catch_error(int const code); +++ +++} // detail namespace +++ +++template +++void try_signal(Fun&& f) +++{ +++ __try +++ { +++ f(); +++ } +++ __except (detail::catch_error(GetExceptionCode())) +++ { +++ throw std::system_error(std::error_code(GetExceptionCode(), seh_category())); +++ } +++} +++ +++} // sig namespace +++ +++#endif +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/deps/try_signal/try_signal_posix.hpp ++@@ -0,0 +1,82 @@ +++/* +++ +++Copyright (c) 2017, Arvid Norberg +++All rights reserved. +++ +++Redistribution and use in source and binary forms, with or without +++modification, are permitted provided that the following conditions +++are met: +++ +++ * Redistributions of source code must retain the above copyright +++ notice, this list of conditions and the following disclaimer. +++ * Redistributions in binary form must reproduce the above copyright +++ notice, this list of conditions and the following disclaimer in +++ the documentation and/or other materials provided with the distribution. +++ * Neither the name of the author nor the names of its +++ contributors may be used to endorse or promote products derived +++ from this software without specific prior written permission. +++ +++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +++AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +++IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +++ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +++LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +++CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +++SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +++INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +++CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +++ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +++POSSIBILITY OF SUCH DAMAGE. +++ +++*/ +++ +++#ifndef TRY_SIGNAL_POSIX_HPP_INCLUDED +++#define TRY_SIGNAL_POSIX_HPP_INCLUDED +++ +++#include "signal_error_code.hpp" +++#include // for sigjmp_buf +++#include +++ +++namespace sig { +++ +++namespace detail { +++ +++extern std::atomic_flag once; +++ +++struct scoped_jmpbuf +++{ +++ explicit scoped_jmpbuf(sigjmp_buf* ptr); +++ ~scoped_jmpbuf(); +++ scoped_jmpbuf(scoped_jmpbuf const&) = delete; +++ scoped_jmpbuf& operator=(scoped_jmpbuf const&) = delete; +++private: +++ sigjmp_buf* _previous_ptr; +++}; +++ +++void handler(int const signo, siginfo_t* si, void*); +++void setup_handler(); +++ +++} // detail namespace +++ +++template +++void try_signal(Fun&& f) +++{ +++ if (sig::detail::once.test_and_set() == false) { +++ sig::detail::setup_handler(); +++ } +++ +++ sigjmp_buf buf; +++ int const sig = sigsetjmp(buf, 1); +++ // set the thread local jmpbuf pointer, and make sure it's cleared when we +++ // leave the scope +++ sig::detail::scoped_jmpbuf scope(&buf); +++ if (sig != 0) +++ throw std::system_error(static_cast(sig)); +++ +++ f(); +++} +++ +++} +++ +++#endif +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/CMakeLists.txt ++@@ -0,0 +1,61 @@ +++project(libsimulator) +++cmake_minimum_required(VERSION 2.8.7) +++ +++set(SRC_DIR src) +++set(TEST_SRC_DIR test) +++set(INCLUDE_DIR include) +++set(CMAKE_CXX_STANDARD 14) +++set(CMAKE_CXX_STANDARD_REQUIRED ON) +++ +++# Boost.System is header-only (since Boost 1.69), so we only need the Boost +++# headers. Requesting the "system" component fails with recent Boost releases +++# that no longer ship a boost_system CMake config file. +++find_package(Boost REQUIRED) +++find_package(Threads REQUIRED) +++ +++set(SIMULATOR_SRC_FILES +++ ${SRC_DIR}/acceptor.cpp +++ ${SRC_DIR}/high_resolution_timer.cpp +++ ${SRC_DIR}/io_service.cpp +++ ${SRC_DIR}/resolver.cpp +++ ${SRC_DIR}/sink_forwarder.cpp +++ ${SRC_DIR}/udp_socket.cpp +++ ${SRC_DIR}/default_config.cpp +++ ${SRC_DIR}/http_proxy.cpp +++ ${SRC_DIR}/pcap.cpp +++ ${SRC_DIR}/simulation.cpp +++ ${SRC_DIR}/socks_server.cpp +++ ${SRC_DIR}/high_resolution_clock.cpp +++ ${SRC_DIR}/http_server.cpp +++ ${SRC_DIR}/queue.cpp +++ ${SRC_DIR}/simulator.cpp +++ ${SRC_DIR}/tcp_socket.cpp +++ ${SRC_DIR}/nat.cpp +++) +++ +++add_library(simulator ${SIMULATOR_SRC_FILES}) +++target_include_directories(simulator PUBLIC ${INCLUDE_DIR}) +++target_link_libraries(simulator PUBLIC Boost::boost) +++ +++if(WIN32) +++ target_link_libraries(simulator PRIVATE ws2_32) +++endif() +++ +++enable_testing() +++ +++function(define_test) +++ set(NAME ${ARGV0}) +++ add_executable(${NAME} ${TEST_SRC_DIR}/${NAME}.cpp ${TEST_SRC_DIR}/main.cpp) +++ target_link_libraries(${NAME} simulator) +++ target_link_libraries(${NAME} Threads::Threads) +++ add_test(NAME ${NAME} COMMAND ${NAME}) +++endfunction() +++ +++define_test(acceptor) +++define_test(multi_accept) +++define_test(multi_homed) +++define_test(null_buffers) +++define_test(parse_request) +++define_test(resolver) +++define_test(timer) +++define_test(udp_socket) ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/Jamfile ++@@ -0,0 +1,136 @@ +++# This Jamfile requires boost-build v2 to build. +++ +++import path ; +++import modules ; +++import os ; +++import testing ; +++ +++BOOST_ROOT = [ modules.peek : BOOST_ROOT ] ; +++ +++ECHO "BOOST_ROOT =" $(BOOST_ROOT) ; +++ECHO "OS =" [ os.name ] ; +++ +++lib wsock32 : : wsock32 shared ; +++lib ws2_32 : : ws2_32 shared ; +++ +++if $(BOOST_ROOT) +++{ +++ use-project /boost : $(BOOST_ROOT) ; +++ alias boost_system : /boost/system//boost_system ; +++} +++else +++{ +++ local boost-lib-search-path = +++ /usr/local/opt/boost/lib +++ /opt/homebrew/lib +++ ; +++ +++ local boost-include-path = +++ /usr/local/opt/boost/include +++ /opt/homebrew/include +++ ; +++ +++ # boost_system is a header-only library now, but we still need to find the boost headers +++ alias boost_system : : : : $(boost-include-path) ; +++} +++ +++SOURCES = +++ simulator +++ simulation +++ io_service +++ high_resolution_timer +++ high_resolution_clock +++ tcp_socket +++ udp_socket +++ queue +++ acceptor +++ default_config +++ http_server +++ socks_server +++ resolver +++ http_proxy +++ sink_forwarder +++ pcap +++ nat +++ ; +++ +++lib simulator +++ : # sources +++ src/$(SOURCES).cpp +++ +++ : # requirements +++ include +++ boost_system +++ windows:ws2_32 +++ windows:wsock32 +++ multi +++ +++ shared:SIMULATOR_BUILDING_SHARED +++ _CRT_SECURE_NO_WARNINGS +++ +++ # https://github.com/chriskohlhoff/asio/issues/290#issuecomment-377727614 +++ _SILENCE_CXX17_ALLOCATOR_VOID_DEPRECATION_WARNING +++ +++ BOOST_ASIO_DISABLE_BOOST_DATE_TIME +++ BOOST_ASIO_HAS_MOVE +++ BOOST_ASIO_ENABLE_CANCELIO +++ +++ # make sure asio uses std::chrono +++ BOOST_ASIO_HAS_STD_CHRONO +++ +++ # io_executor only implements the legacy (Networking TS v1) executor +++ # concept: this falls any_io_executor back to boost::asio::executor, +++ # which it can wrap, so boost::asio::ssl::stream's bookkeeping timers +++ # work for consumers that include simulator/ssl.hpp. +++ BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT +++ +++ # disable auto-link +++ BOOST_ALL_NO_LIB +++ +++ : # default build +++ all +++ on +++ # boost.asio has a global tss_ptr which is the head of a +++ # linked list of all invocations of run on that thread. This +++ # determines whether dispatch() will run the handler immediately +++ # not. The simulator relies on this. On windows each DLL will have +++ # its own copy of this variable, effectively causing a deadlock +++ # in the simulator. So, default to static linking +++ static +++ +++ : # usage requirements +++ BOOST_ASIO_DISABLE_BOOST_DATE_TIME +++ BOOST_ASIO_HAS_MOVE +++ BOOST_ASIO_ENABLE_CANCELIO +++ BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT +++ multi +++ include +++ shared:SIMULATOR_LINKING_SHARED +++ +++ # https://github.com/chriskohlhoff/asio/issues/290#issuecomment-377727614 +++ _SILENCE_CXX17_ALLOCATOR_VOID_DEPRECATION_WARNING +++ ; +++ +++project +++ : requirements +++ simulator +++ # disable auto-link +++ BOOST_ALL_NO_LIB +++ : default-build +++ static +++ multi +++ 14 +++ ; +++ +++test-suite simulator-tests : [ run +++ test/main.cpp +++ test/resolver.cpp +++ test/multi_homed.cpp +++ test/timer.cpp +++ test/acceptor.cpp +++ test/multi_accept.cpp +++ test/null_buffers.cpp +++ test/udp_socket.cpp +++ test/parse_request.cpp +++ ] ; +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/LICENSE ++@@ -0,0 +1,675 @@ +++ GNU GENERAL PUBLIC LICENSE +++ Version 3, 29 June 2007 +++ +++ Copyright (C) 2007 Free Software Foundation, Inc. +++ Everyone is permitted to copy and distribute verbatim copies +++ of this license document, but changing it is not allowed. +++ +++ Preamble +++ +++ The GNU General Public License is a free, copyleft license for +++software and other kinds of works. +++ +++ The licenses for most software and other practical works are designed +++to take away your freedom to share and change the works. By contrast, +++the GNU General Public License is intended to guarantee your freedom to +++share and change all versions of a program--to make sure it remains free +++software for all its users. We, the Free Software Foundation, use the +++GNU General Public License for most of our software; it applies also to +++any other work released this way by its authors. You can apply it to +++your programs, too. +++ +++ When we speak of free software, we are referring to freedom, not +++price. Our General Public Licenses are designed to make sure that you +++have the freedom to distribute copies of free software (and charge for +++them if you wish), that you receive source code or can get it if you +++want it, that you can change the software or use pieces of it in new +++free programs, and that you know you can do these things. +++ +++ To protect your rights, we need to prevent others from denying you +++these rights or asking you to surrender the rights. Therefore, you have +++certain responsibilities if you distribute copies of the software, or if +++you modify it: responsibilities to respect the freedom of others. +++ +++ For example, if you distribute copies of such a program, whether +++gratis or for a fee, you must pass on to the recipients the same +++freedoms that you received. You must make sure that they, too, receive +++or can get the source code. And you must show them these terms so they +++know their rights. +++ +++ Developers that use the GNU GPL protect your rights with two steps: +++(1) assert copyright on the software, and (2) offer you this License +++giving you legal permission to copy, distribute and/or modify it. +++ +++ For the developers' and authors' protection, the GPL clearly explains +++that there is no warranty for this free software. For both users' and +++authors' sake, the GPL requires that modified versions be marked as +++changed, so that their problems will not be attributed erroneously to +++authors of previous versions. +++ +++ Some devices are designed to deny users access to install or run +++modified versions of the software inside them, although the manufacturer +++can do so. This is fundamentally incompatible with the aim of +++protecting users' freedom to change the software. The systematic +++pattern of such abuse occurs in the area of products for individuals to +++use, which is precisely where it is most unacceptable. Therefore, we +++have designed this version of the GPL to prohibit the practice for those +++products. If such problems arise substantially in other domains, we +++stand ready to extend this provision to those domains in future versions +++of the GPL, as needed to protect the freedom of users. +++ +++ Finally, every program is threatened constantly by software patents. +++States should not allow patents to restrict development and use of +++software on general-purpose computers, but in those that do, we wish to +++avoid the special danger that patents applied to a free program could +++make it effectively proprietary. To prevent this, the GPL assures that +++patents cannot be used to render the program non-free. +++ +++ The precise terms and conditions for copying, distribution and +++modification follow. +++ +++ TERMS AND CONDITIONS +++ +++ 0. Definitions. +++ +++ "This License" refers to version 3 of the GNU General Public License. +++ +++ "Copyright" also means copyright-like laws that apply to other kinds of +++works, such as semiconductor masks. +++ +++ "The Program" refers to any copyrightable work licensed under this +++License. Each licensee is addressed as "you". "Licensees" and +++"recipients" may be individuals or organizations. +++ +++ To "modify" a work means to copy from or adapt all or part of the work +++in a fashion requiring copyright permission, other than the making of an +++exact copy. The resulting work is called a "modified version" of the +++earlier work or a work "based on" the earlier work. +++ +++ A "covered work" means either the unmodified Program or a work based +++on the Program. +++ +++ To "propagate" a work means to do anything with it that, without +++permission, would make you directly or secondarily liable for +++infringement under applicable copyright law, except executing it on a +++computer or modifying a private copy. Propagation includes copying, +++distribution (with or without modification), making available to the +++public, and in some countries other activities as well. +++ +++ To "convey" a work means any kind of propagation that enables other +++parties to make or receive copies. Mere interaction with a user through +++a computer network, with no transfer of a copy, is not conveying. +++ +++ An interactive user interface displays "Appropriate Legal Notices" +++to the extent that it includes a convenient and prominently visible +++feature that (1) displays an appropriate copyright notice, and (2) +++tells the user that there is no warranty for the work (except to the +++extent that warranties are provided), that licensees may convey the +++work under this License, and how to view a copy of this License. If +++the interface presents a list of user commands or options, such as a +++menu, a prominent item in the list meets this criterion. +++ +++ 1. Source Code. +++ +++ The "source code" for a work means the preferred form of the work +++for making modifications to it. "Object code" means any non-source +++form of a work. +++ +++ A "Standard Interface" means an interface that either is an official +++standard defined by a recognized standards body, or, in the case of +++interfaces specified for a particular programming language, one that +++is widely used among developers working in that language. +++ +++ The "System Libraries" of an executable work include anything, other +++than the work as a whole, that (a) is included in the normal form of +++packaging a Major Component, but which is not part of that Major +++Component, and (b) serves only to enable use of the work with that +++Major Component, or to implement a Standard Interface for which an +++implementation is available to the public in source code form. A +++"Major Component", in this context, means a major essential component +++(kernel, window system, and so on) of the specific operating system +++(if any) on which the executable work runs, or a compiler used to +++produce the work, or an object code interpreter used to run it. +++ +++ The "Corresponding Source" for a work in object code form means all +++the source code needed to generate, install, and (for an executable +++work) run the object code and to modify the work, including scripts to +++control those activities. However, it does not include the work's +++System Libraries, or general-purpose tools or generally available free +++programs which are used unmodified in performing those activities but +++which are not part of the work. For example, Corresponding Source +++includes interface definition files associated with source files for +++the work, and the source code for shared libraries and dynamically +++linked subprograms that the work is specifically designed to require, +++such as by intimate data communication or control flow between those +++subprograms and other parts of the work. +++ +++ The Corresponding Source need not include anything that users +++can regenerate automatically from other parts of the Corresponding +++Source. +++ +++ The Corresponding Source for a work in source code form is that +++same work. +++ +++ 2. Basic Permissions. +++ +++ All rights granted under this License are granted for the term of +++copyright on the Program, and are irrevocable provided the stated +++conditions are met. This License explicitly affirms your unlimited +++permission to run the unmodified Program. The output from running a +++covered work is covered by this License only if the output, given its +++content, constitutes a covered work. This License acknowledges your +++rights of fair use or other equivalent, as provided by copyright law. +++ +++ You may make, run and propagate covered works that you do not +++convey, without conditions so long as your license otherwise remains +++in force. You may convey covered works to others for the sole purpose +++of having them make modifications exclusively for you, or provide you +++with facilities for running those works, provided that you comply with +++the terms of this License in conveying all material for which you do +++not control copyright. Those thus making or running the covered works +++for you must do so exclusively on your behalf, under your direction +++and control, on terms that prohibit them from making any copies of +++your copyrighted material outside their relationship with you. +++ +++ Conveying under any other circumstances is permitted solely under +++the conditions stated below. Sublicensing is not allowed; section 10 +++makes it unnecessary. +++ +++ 3. Protecting Users' Legal Rights From Anti-Circumvention Law. +++ +++ No covered work shall be deemed part of an effective technological +++measure under any applicable law fulfilling obligations under article +++11 of the WIPO copyright treaty adopted on 20 December 1996, or +++similar laws prohibiting or restricting circumvention of such +++measures. +++ +++ When you convey a covered work, you waive any legal power to forbid +++circumvention of technological measures to the extent such circumvention +++is effected by exercising rights under this License with respect to +++the covered work, and you disclaim any intention to limit operation or +++modification of the work as a means of enforcing, against the work's +++users, your or third parties' legal rights to forbid circumvention of +++technological measures. +++ +++ 4. Conveying Verbatim Copies. +++ +++ You may convey verbatim copies of the Program's source code as you +++receive it, in any medium, provided that you conspicuously and +++appropriately publish on each copy an appropriate copyright notice; +++keep intact all notices stating that this License and any +++non-permissive terms added in accord with section 7 apply to the code; +++keep intact all notices of the absence of any warranty; and give all +++recipients a copy of this License along with the Program. +++ +++ You may charge any price or no price for each copy that you convey, +++and you may offer support or warranty protection for a fee. +++ +++ 5. Conveying Modified Source Versions. +++ +++ You may convey a work based on the Program, or the modifications to +++produce it from the Program, in the form of source code under the +++terms of section 4, provided that you also meet all of these conditions: +++ +++ a) The work must carry prominent notices stating that you modified +++ it, and giving a relevant date. +++ +++ b) The work must carry prominent notices stating that it is +++ released under this License and any conditions added under section +++ 7. This requirement modifies the requirement in section 4 to +++ "keep intact all notices". +++ +++ c) You must license the entire work, as a whole, under this +++ License to anyone who comes into possession of a copy. This +++ License will therefore apply, along with any applicable section 7 +++ additional terms, to the whole of the work, and all its parts, +++ regardless of how they are packaged. This License gives no +++ permission to license the work in any other way, but it does not +++ invalidate such permission if you have separately received it. +++ +++ d) If the work has interactive user interfaces, each must display +++ Appropriate Legal Notices; however, if the Program has interactive +++ interfaces that do not display Appropriate Legal Notices, your +++ work need not make them do so. +++ +++ A compilation of a covered work with other separate and independent +++works, which are not by their nature extensions of the covered work, +++and which are not combined with it such as to form a larger program, +++in or on a volume of a storage or distribution medium, is called an +++"aggregate" if the compilation and its resulting copyright are not +++used to limit the access or legal rights of the compilation's users +++beyond what the individual works permit. Inclusion of a covered work +++in an aggregate does not cause this License to apply to the other +++parts of the aggregate. +++ +++ 6. Conveying Non-Source Forms. +++ +++ You may convey a covered work in object code form under the terms +++of sections 4 and 5, provided that you also convey the +++machine-readable Corresponding Source under the terms of this License, +++in one of these ways: +++ +++ a) Convey the object code in, or embodied in, a physical product +++ (including a physical distribution medium), accompanied by the +++ Corresponding Source fixed on a durable physical medium +++ customarily used for software interchange. +++ +++ b) Convey the object code in, or embodied in, a physical product +++ (including a physical distribution medium), accompanied by a +++ written offer, valid for at least three years and valid for as +++ long as you offer spare parts or customer support for that product +++ model, to give anyone who possesses the object code either (1) a +++ copy of the Corresponding Source for all the software in the +++ product that is covered by this License, on a durable physical +++ medium customarily used for software interchange, for a price no +++ more than your reasonable cost of physically performing this +++ conveying of source, or (2) access to copy the +++ Corresponding Source from a network server at no charge. +++ +++ c) Convey individual copies of the object code with a copy of the +++ written offer to provide the Corresponding Source. This +++ alternative is allowed only occasionally and noncommercially, and +++ only if you received the object code with such an offer, in accord +++ with subsection 6b. +++ +++ d) Convey the object code by offering access from a designated +++ place (gratis or for a charge), and offer equivalent access to the +++ Corresponding Source in the same way through the same place at no +++ further charge. You need not require recipients to copy the +++ Corresponding Source along with the object code. If the place to +++ copy the object code is a network server, the Corresponding Source +++ may be on a different server (operated by you or a third party) +++ that supports equivalent copying facilities, provided you maintain +++ clear directions next to the object code saying where to find the +++ Corresponding Source. Regardless of what server hosts the +++ Corresponding Source, you remain obligated to ensure that it is +++ available for as long as needed to satisfy these requirements. +++ +++ e) Convey the object code using peer-to-peer transmission, provided +++ you inform other peers where the object code and Corresponding +++ Source of the work are being offered to the general public at no +++ charge under subsection 6d. +++ +++ A separable portion of the object code, whose source code is excluded +++from the Corresponding Source as a System Library, need not be +++included in conveying the object code work. +++ +++ A "User Product" is either (1) a "consumer product", which means any +++tangible personal property which is normally used for personal, family, +++or household purposes, or (2) anything designed or sold for incorporation +++into a dwelling. In determining whether a product is a consumer product, +++doubtful cases shall be resolved in favor of coverage. For a particular +++product received by a particular user, "normally used" refers to a +++typical or common use of that class of product, regardless of the status +++of the particular user or of the way in which the particular user +++actually uses, or expects or is expected to use, the product. A product +++is a consumer product regardless of whether the product has substantial +++commercial, industrial or non-consumer uses, unless such uses represent +++the only significant mode of use of the product. +++ +++ "Installation Information" for a User Product means any methods, +++procedures, authorization keys, or other information required to install +++and execute modified versions of a covered work in that User Product from +++a modified version of its Corresponding Source. The information must +++suffice to ensure that the continued functioning of the modified object +++code is in no case prevented or interfered with solely because +++modification has been made. +++ +++ If you convey an object code work under this section in, or with, or +++specifically for use in, a User Product, and the conveying occurs as +++part of a transaction in which the right of possession and use of the +++User Product is transferred to the recipient in perpetuity or for a +++fixed term (regardless of how the transaction is characterized), the +++Corresponding Source conveyed under this section must be accompanied +++by the Installation Information. But this requirement does not apply +++if neither you nor any third party retains the ability to install +++modified object code on the User Product (for example, the work has +++been installed in ROM). +++ +++ The requirement to provide Installation Information does not include a +++requirement to continue to provide support service, warranty, or updates +++for a work that has been modified or installed by the recipient, or for +++the User Product in which it has been modified or installed. Access to a +++network may be denied when the modification itself materially and +++adversely affects the operation of the network or violates the rules and +++protocols for communication across the network. +++ +++ Corresponding Source conveyed, and Installation Information provided, +++in accord with this section must be in a format that is publicly +++documented (and with an implementation available to the public in +++source code form), and must require no special password or key for +++unpacking, reading or copying. +++ +++ 7. Additional Terms. +++ +++ "Additional permissions" are terms that supplement the terms of this +++License by making exceptions from one or more of its conditions. +++Additional permissions that are applicable to the entire Program shall +++be treated as though they were included in this License, to the extent +++that they are valid under applicable law. If additional permissions +++apply only to part of the Program, that part may be used separately +++under those permissions, but the entire Program remains governed by +++this License without regard to the additional permissions. +++ +++ When you convey a copy of a covered work, you may at your option +++remove any additional permissions from that copy, or from any part of +++it. (Additional permissions may be written to require their own +++removal in certain cases when you modify the work.) You may place +++additional permissions on material, added by you to a covered work, +++for which you have or can give appropriate copyright permission. +++ +++ Notwithstanding any other provision of this License, for material you +++add to a covered work, you may (if authorized by the copyright holders of +++that material) supplement the terms of this License with terms: +++ +++ a) Disclaiming warranty or limiting liability differently from the +++ terms of sections 15 and 16 of this License; or +++ +++ b) Requiring preservation of specified reasonable legal notices or +++ author attributions in that material or in the Appropriate Legal +++ Notices displayed by works containing it; or +++ +++ c) Prohibiting misrepresentation of the origin of that material, or +++ requiring that modified versions of such material be marked in +++ reasonable ways as different from the original version; or +++ +++ d) Limiting the use for publicity purposes of names of licensors or +++ authors of the material; or +++ +++ e) Declining to grant rights under trademark law for use of some +++ trade names, trademarks, or service marks; or +++ +++ f) Requiring indemnification of licensors and authors of that +++ material by anyone who conveys the material (or modified versions of +++ it) with contractual assumptions of liability to the recipient, for +++ any liability that these contractual assumptions directly impose on +++ those licensors and authors. +++ +++ All other non-permissive additional terms are considered "further +++restrictions" within the meaning of section 10. If the Program as you +++received it, or any part of it, contains a notice stating that it is +++governed by this License along with a term that is a further +++restriction, you may remove that term. If a license document contains +++a further restriction but permits relicensing or conveying under this +++License, you may add to a covered work material governed by the terms +++of that license document, provided that the further restriction does +++not survive such relicensing or conveying. +++ +++ If you add terms to a covered work in accord with this section, you +++must place, in the relevant source files, a statement of the +++additional terms that apply to those files, or a notice indicating +++where to find the applicable terms. +++ +++ Additional terms, permissive or non-permissive, may be stated in the +++form of a separately written license, or stated as exceptions; +++the above requirements apply either way. +++ +++ 8. Termination. +++ +++ You may not propagate or modify a covered work except as expressly +++provided under this License. Any attempt otherwise to propagate or +++modify it is void, and will automatically terminate your rights under +++this License (including any patent licenses granted under the third +++paragraph of section 11). +++ +++ However, if you cease all violation of this License, then your +++license from a particular copyright holder is reinstated (a) +++provisionally, unless and until the copyright holder explicitly and +++finally terminates your license, and (b) permanently, if the copyright +++holder fails to notify you of the violation by some reasonable means +++prior to 60 days after the cessation. +++ +++ Moreover, your license from a particular copyright holder is +++reinstated permanently if the copyright holder notifies you of the +++violation by some reasonable means, this is the first time you have +++received notice of violation of this License (for any work) from that +++copyright holder, and you cure the violation prior to 30 days after +++your receipt of the notice. +++ +++ Termination of your rights under this section does not terminate the +++licenses of parties who have received copies or rights from you under +++this License. If your rights have been terminated and not permanently +++reinstated, you do not qualify to receive new licenses for the same +++material under section 10. +++ +++ 9. Acceptance Not Required for Having Copies. +++ +++ You are not required to accept this License in order to receive or +++run a copy of the Program. Ancillary propagation of a covered work +++occurring solely as a consequence of using peer-to-peer transmission +++to receive a copy likewise does not require acceptance. However, +++nothing other than this License grants you permission to propagate or +++modify any covered work. These actions infringe copyright if you do +++not accept this License. Therefore, by modifying or propagating a +++covered work, you indicate your acceptance of this License to do so. +++ +++ 10. Automatic Licensing of Downstream Recipients. +++ +++ Each time you convey a covered work, the recipient automatically +++receives a license from the original licensors, to run, modify and +++propagate that work, subject to this License. You are not responsible +++for enforcing compliance by third parties with this License. +++ +++ An "entity transaction" is a transaction transferring control of an +++organization, or substantially all assets of one, or subdividing an +++organization, or merging organizations. If propagation of a covered +++work results from an entity transaction, each party to that +++transaction who receives a copy of the work also receives whatever +++licenses to the work the party's predecessor in interest had or could +++give under the previous paragraph, plus a right to possession of the +++Corresponding Source of the work from the predecessor in interest, if +++the predecessor has it or can get it with reasonable efforts. +++ +++ You may not impose any further restrictions on the exercise of the +++rights granted or affirmed under this License. For example, you may +++not impose a license fee, royalty, or other charge for exercise of +++rights granted under this License, and you may not initiate litigation +++(including a cross-claim or counterclaim in a lawsuit) alleging that +++any patent claim is infringed by making, using, selling, offering for +++sale, or importing the Program or any portion of it. +++ +++ 11. Patents. +++ +++ A "contributor" is a copyright holder who authorizes use under this +++License of the Program or a work on which the Program is based. The +++work thus licensed is called the contributor's "contributor version". +++ +++ A contributor's "essential patent claims" are all patent claims +++owned or controlled by the contributor, whether already acquired or +++hereafter acquired, that would be infringed by some manner, permitted +++by this License, of making, using, or selling its contributor version, +++but do not include claims that would be infringed only as a +++consequence of further modification of the contributor version. For +++purposes of this definition, "control" includes the right to grant +++patent sublicenses in a manner consistent with the requirements of +++this License. +++ +++ Each contributor grants you a non-exclusive, worldwide, royalty-free +++patent license under the contributor's essential patent claims, to +++make, use, sell, offer for sale, import and otherwise run, modify and +++propagate the contents of its contributor version. +++ +++ In the following three paragraphs, a "patent license" is any express +++agreement or commitment, however denominated, not to enforce a patent +++(such as an express permission to practice a patent or covenant not to +++sue for patent infringement). To "grant" such a patent license to a +++party means to make such an agreement or commitment not to enforce a +++patent against the party. +++ +++ If you convey a covered work, knowingly relying on a patent license, +++and the Corresponding Source of the work is not available for anyone +++to copy, free of charge and under the terms of this License, through a +++publicly available network server or other readily accessible means, +++then you must either (1) cause the Corresponding Source to be so +++available, or (2) arrange to deprive yourself of the benefit of the +++patent license for this particular work, or (3) arrange, in a manner +++consistent with the requirements of this License, to extend the patent +++license to downstream recipients. "Knowingly relying" means you have +++actual knowledge that, but for the patent license, your conveying the +++covered work in a country, or your recipient's use of the covered work +++in a country, would infringe one or more identifiable patents in that +++country that you have reason to believe are valid. +++ +++ If, pursuant to or in connection with a single transaction or +++arrangement, you convey, or propagate by procuring conveyance of, a +++covered work, and grant a patent license to some of the parties +++receiving the covered work authorizing them to use, propagate, modify +++or convey a specific copy of the covered work, then the patent license +++you grant is automatically extended to all recipients of the covered +++work and works based on it. +++ +++ A patent license is "discriminatory" if it does not include within +++the scope of its coverage, prohibits the exercise of, or is +++conditioned on the non-exercise of one or more of the rights that are +++specifically granted under this License. You may not convey a covered +++work if you are a party to an arrangement with a third party that is +++in the business of distributing software, under which you make payment +++to the third party based on the extent of your activity of conveying +++the work, and under which the third party grants, to any of the +++parties who would receive the covered work from you, a discriminatory +++patent license (a) in connection with copies of the covered work +++conveyed by you (or copies made from those copies), or (b) primarily +++for and in connection with specific products or compilations that +++contain the covered work, unless you entered into that arrangement, +++or that patent license was granted, prior to 28 March 2007. +++ +++ Nothing in this License shall be construed as excluding or limiting +++any implied license or other defenses to infringement that may +++otherwise be available to you under applicable patent law. +++ +++ 12. No Surrender of Others' Freedom. +++ +++ If conditions are imposed on you (whether by court order, agreement or +++otherwise) that contradict the conditions of this License, they do not +++excuse you from the conditions of this License. If you cannot convey a +++covered work so as to satisfy simultaneously your obligations under this +++License and any other pertinent obligations, then as a consequence you may +++not convey it at all. For example, if you agree to terms that obligate you +++to collect a royalty for further conveying from those to whom you convey +++the Program, the only way you could satisfy both those terms and this +++License would be to refrain entirely from conveying the Program. +++ +++ 13. Use with the GNU Affero General Public License. +++ +++ Notwithstanding any other provision of this License, you have +++permission to link or combine any covered work with a work licensed +++under version 3 of the GNU Affero General Public License into a single +++combined work, and to convey the resulting work. The terms of this +++License will continue to apply to the part which is the covered work, +++but the special requirements of the GNU Affero General Public License, +++section 13, concerning interaction through a network will apply to the +++combination as such. +++ +++ 14. Revised Versions of this License. +++ +++ The Free Software Foundation may publish revised and/or new versions of +++the GNU General Public License from time to time. Such new versions will +++be similar in spirit to the present version, but may differ in detail to +++address new problems or concerns. +++ +++ Each version is given a distinguishing version number. If the +++Program specifies that a certain numbered version of the GNU General +++Public License "or any later version" applies to it, you have the +++option of following the terms and conditions either of that numbered +++version or of any later version published by the Free Software +++Foundation. If the Program does not specify a version number of the +++GNU General Public License, you may choose any version ever published +++by the Free Software Foundation. +++ +++ If the Program specifies that a proxy can decide which future +++versions of the GNU General Public License can be used, that proxy's +++public statement of acceptance of a version permanently authorizes you +++to choose that version for the Program. +++ +++ Later license versions may give you additional or different +++permissions. However, no additional obligations are imposed on any +++author or copyright holder as a result of your choosing to follow a +++later version. +++ +++ 15. Disclaimer of Warranty. +++ +++ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +++APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +++HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +++OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +++THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +++PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +++IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +++ALL NECESSARY SERVICING, REPAIR OR CORRECTION. +++ +++ 16. Limitation of Liability. +++ +++ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +++WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +++THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +++GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +++USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +++DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +++PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +++EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +++SUCH DAMAGES. +++ +++ 17. Interpretation of Sections 15 and 16. +++ +++ If the disclaimer of warranty and limitation of liability provided +++above cannot be given local legal effect according to their terms, +++reviewing courts shall apply local law that most closely approximates +++an absolute waiver of all civil liability in connection with the +++Program, unless a warranty or assumption of liability accompanies a +++copy of the Program in return for a fee. +++ +++ END OF TERMS AND CONDITIONS +++ +++ How to Apply These Terms to Your New Programs +++ +++ If you develop a new program, and you want it to be of the greatest +++possible use to the public, the best way to achieve this is to make it +++free software which everyone can redistribute and change under these terms. +++ +++ To do so, attach the following notices to the program. It is safest +++to attach them to the start of each source file to most effectively +++state the exclusion of warranty; and each file should have at least +++the "copyright" line and a pointer to where the full notice is found. +++ +++ {one line to give the program's name and a brief idea of what it does.} +++ Copyright (C) {year} {name of author} +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++Also add information on how to contact you by electronic and paper mail. +++ +++ If the program does terminal interaction, make it output a short +++notice like this when it starts in an interactive mode: +++ +++ {project} Copyright (C) {year} {fullname} +++ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. +++ This is free software, and you are welcome to redistribute it +++ under certain conditions; type `show c' for details. +++ +++The hypothetical commands `show w' and `show c' should show the appropriate +++parts of the General Public License. Of course, your program's commands +++might be different; for a GUI interface, you would use an "about box". +++ +++ You should also get your employer (if you work as a programmer) or school, +++if any, to sign a "copyright disclaimer" for the program, if necessary. +++For more information on this, and how to apply and follow the GNU GPL, see +++. +++ +++ The GNU General Public License does not permit incorporating your program +++into proprietary programs. If your program is a subroutine library, you +++may consider it more useful to permit linking proprietary applications with +++the library. If this is what you want to do, use the GNU Lesser General +++Public License instead of this License. But first, please read +++. +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/README.rst ++@@ -0,0 +1,199 @@ +++libsimulator +++============ +++ +++.. image:: https://github.com/arvidn/libsimulator/actions/workflows/ci.yml/badge.svg?branch=master +++ :target: https://github.com/arvidn/libsimulator/actions/workflows/ci.yml +++ +++*This is still in initial development, some of this README represents ambitions +++rather than the current state* +++ +++libsimulator is a library for running discrete event simulations, implementing +++the ``boost.asio`` API (or a somewhat faithful emulation of a subset of it, +++patches are welcome). This makes it practical to be used as a testing tool of +++real implementations of network software as well as for writing simulators that +++later turn into live production applications. +++ +++The simulation has to have a single time-line to be deterministic, meaning it +++must be single threaded and use a single ``io_service`` as the message queue. +++These requirements may affect how the program to be tested is written. It may +++for instance require that an external io_service can be provided rather than one +++being wrapped in an internal thread. +++ +++However, ``boost.asio`` programs may generally benefit from being transformed to +++this form, as the become *composable*, i.e. agnostic to which io_service they +++run on or how many threads are running it. +++ +++features +++-------- +++ +++The currently (partially) supported classes are: +++ +++* chrono::high_resolution_clock +++* asio::high_resolution_timer +++* asio::ip::tcp::acceptor +++* asio::ip::tcp::endpoint +++* asio::ip::address (v4 and v6 variants, these just defer to the actual +++ boost.asio types) +++* asio::ip::tcp::socket +++* asio::ip::udp::socket +++* asio::io_service +++* asio::ip::udp::resolver +++* asio::ip::tcp::resolver +++ +++The ``high_resolution_clock`` in the ``chrono`` namespace implements the timer +++concept from the chrono library. +++ +++usage +++----- +++ +++The ``io_service`` object is significantly different from the one in boost.asio. +++This is because one simulation may only have a single message loop and a single +++ordering of events. This single message loop is provided by the ``simulation`` +++class. Each simulation should have only one such object. An ``io_service`` +++object represents a single node on the network. When creating an io_service, you +++have to pass in the simulation it belongs to as well as the IP address it should +++have. It is also possible to pass in multiple addresses to form a multi-homed +++node. For instance, one with both an IPv4 and IPv6 interface. +++ +++When creating sockets, binding and connecting them, the io_service object +++determines what ``INADDR_ANY`` resolves to (the first IP assigned to that node). +++ +++The only aspects of the io_service interface that's preserved are ``post()``, +++``dispatch()`` and constructing timers and sockets. In short, the ``run()`` and +++``poll()`` family of functions do not exist. Every io_service object is assumed +++to be run, and all of their events are handled by the simulation object. +++ +++None of the synchronous APIs are supported, because that would require +++integration with OS threads and scheduler. +++ +++example +++------- +++ +++Here's a simple example illustrating the asio timer:: +++ +++ #include "simulator/simulator.hpp" +++ #include +++ #include +++ +++ void print_time(sim::asio::high_resolution_timer& timer +++ , boost::system::error_code const& ec) +++ { +++ using namespace sim::chrono; +++ static int counter = 0; +++ +++ printf("[%d] timer fired at: %d milliseconds. error: %s\n" +++ , counter +++ , int(duration_cast(high_resolution_clock::now() +++ .time_since_epoch()).count()) +++ , ec.message().c_str()); +++ +++ ++counter; +++ if (counter < 5) +++ { +++ timer.expires_from_now(seconds(counter)); +++ timer.async_wait(std::bind(&print_time, std::ref(timer), _1)); +++ } +++ } +++ +++ int main() +++ { +++ using namespace sim::chrono; +++ +++ default_config cfg; +++ simulation sim(cfg); +++ io_service ios(sim, ip::address_v4::from_string("1.2.3.4")); +++ sim::asio::high_resolution_timer timer(ios); +++ +++ timer.expires_from_now(seconds(1)); +++ timer.async_wait(std::bind(&print_time, std::ref(timer), _1)); +++ +++ boost::system::error_code ec; +++ sim.run(ec); +++ +++ printf("sim::run() returned: %s at: %d\n" +++ , ec.message().c_str() +++ , int(duration_cast(high_resolution_clock::now() +++ .time_since_epoch()).count())); +++ } +++ +++The output from this program is:: +++ +++ [0] timer fired at: 1000 milliseconds. error: Undefined error: 0 +++ [1] timer fired at: 2000 milliseconds. error: Undefined error: 0 +++ [2] timer fired at: 4000 milliseconds. error: Undefined error: 0 +++ [3] timer fired at: 7000 milliseconds. error: Undefined error: 0 +++ [4] timer fired at: 11000 milliseconds. error: Undefined error: 0 +++ io_service::run() returned: Undefined error: 0 at: 11000 +++ +++And obviously it doesn't take 11 wall-clock seconds to run (it returns +++instantly). +++ +++configuration +++------------- +++ +++The simulated network can be configured with per-node pair bandwidth, round-trip +++latency and queue sizes. This is controlled via a callback interface that +++libsimulator will ask for these properties when nodes get connected. +++ +++The resolution of hostnames is also configurable by providing a callback on the +++configuration object along with the latency of individual lookups. +++ +++To configure the network for the simulation, pass in a reference to an object +++implementing the ``sim::configuration`` interface:: +++ +++ struct configuration +++ { +++ // build the network +++ virtual void build(simulation& sim) = 0; +++ +++ // return the hops on the network packets from src to dst need to traverse +++ virtual route channel_route(asio::ip::address src +++ , asio::ip::address dst) = 0; +++ +++ // return the hops an incoming packet to ep need to traverse before +++ // reaching the socket (for instance a NAT) +++ virtual route incoming_route(asio::ip::address ip) = 0; +++ +++ // return the hops an outgoing packet from ep need to traverse before +++ // reaching the network (for instance a DSL modem) +++ virtual route outgoing_route(asio::ip::address ip) = 0; +++ +++ // return the path MTU between the two IP addresses +++ // For TCP sockets, this will be called once when the connection is +++ // established. For UDP sockets it's called for every burst of packets +++ // that are sent +++ virtual int path_mtu(asio::ip::address ip1, asio::ip::address ip2) = 0; +++ +++ // called for every hostname lookup made by the client. ``reqyestor`` is +++ // the node performing the lookup, ``hostname`` is the name being looked +++ // up. Resolve the name into addresses and fill in ``result`` or set +++ // ``ec`` if the hostname is not found or some other error occurs. The +++ // return value is the latency of the lookup. The client's callback won't +++ // be called until after waiting this long. +++ virtual chrono::high_resolution_clock::duration hostname_lookup( +++ asio::ip::address const& requestor +++ , std::string hostname +++ , std::vector& result +++ , boost::system::error_code& ec) = 0; +++ }; +++ +++``build()`` is called right after the simulation is constructed. It gives the +++configuration object an opportunity to construct the core queues, since they +++need access to the simulator. +++ +++``channel_route()`` is expected to return a *route* of network hops from the +++source IP to the destination IP. A route is a series of ``sink`` objects. The +++typical sink is a ``sim::queue``, which is a network node with a specific rate +++limit, propagation delay and queue size. +++ +++*TODO: finish document configuration interface* +++ +++history +++------- +++ +++libsimulator grew out of libtorrent's unit tests, as a tool to make them reliable +++and deterministic (i.e. not depend on external systems like sockets and timers) +++and also easier to debug. The subset of the asio API initially supported by this +++library is the subset used by libtorrent. Patches are welcome to improve +++fidelity and support. +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/chrono.hpp ++@@ -0,0 +1,87 @@ +++/* +++ +++Copyright (c) 2015, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#ifndef CHRONO_HPP_INCLUDED +++#define CHRONO_HPP_INCLUDED +++ +++#include +++#include "simulator/config.hpp" +++ +++#if defined BOOST_ASIO_HAS_STD_CHRONO +++#include +++#else +++#include "simulator/push_warnings.hpp" +++ +++#include +++#include +++#include +++ +++#include "simulator/pop_warnings.hpp" +++#endif +++ +++namespace sim { namespace chrono +++{ +++#if defined BOOST_ASIO_HAS_STD_CHRONO +++ using std::chrono::seconds; +++ using std::chrono::milliseconds; +++ using std::chrono::microseconds; +++ using std::chrono::nanoseconds; +++ using std::chrono::minutes; +++ using std::chrono::hours; +++ using std::chrono::duration_cast; +++ using std::chrono::time_point; +++ using std::chrono::duration; +++#else +++ using boost::chrono::seconds; +++ using boost::chrono::milliseconds; +++ using boost::chrono::microseconds; +++ using boost::chrono::nanoseconds; +++ using boost::chrono::minutes; +++ using boost::chrono::hours; +++ using boost::chrono::duration_cast; +++ using boost::chrono::time_point; +++ using boost::chrono::duration; +++#endif +++ +++ // std.chrono / boost.chrono compatible high_resolution_clock using a simulated time +++ struct SIMULATOR_DECL high_resolution_clock +++ { +++ using rep = std::int64_t; +++#if defined BOOST_ASIO_HAS_STD_CHRONO +++ using period = std::nano; +++ using time_point = std::chrono::time_point; +++ using duration = std::chrono::duration; +++#else +++ using period = boost::nano; +++ using time_point = time_point; +++ using duration = duration; +++#endif +++ static const bool is_steady = true; +++ static time_point now(); +++ +++ // private interface +++ static void fast_forward(high_resolution_clock::duration d); +++ }; +++ +++ SIMULATOR_DECL void reset_clock(); +++ +++} // chrono +++} // sim +++ +++#endif +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/config.hpp ++@@ -0,0 +1,51 @@ +++/* +++ +++Copyright (c) 2015, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#ifndef CONFIG_HPP_INCLUDED +++#define CONFIG_HPP_INCLUDED +++ +++#include "simulator/push_warnings.hpp" +++#include +++#include "simulator/pop_warnings.hpp" +++ +++#ifdef SIMULATOR_BUILDING_SHARED +++#define SIMULATOR_DECL BOOST_SYMBOL_EXPORT +++#elif defined SIMULATOR_LINKING_SHARED +++#define SIMULATOR_DECL BOOST_SYMBOL_IMPORT +++#else +++#define SIMULATOR_DECL +++#endif +++ +++#if defined __clang__ || defined __GNUC__ +++#define LIBSIMULATOR_NO_RETURN __attribute((noreturn)) +++#elif _MSC_VER +++#define LIBSIMULATOR_NO_RETURN __declspec(noreturn) +++#else +++#define LIBSIMULATOR_NO_RETURN +++#endif +++ +++#ifdef _MSC_VER +++#pragma warning(push) +++// warning C4251: X: class Y needs to have dll-interface to be used by clients of struct +++#pragma warning( disable : 4251) +++// warning C4661: X: no suitable definition provided for explicit template instantiation request +++#pragma warning( disable : 4661) +++#endif +++ +++#endif +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/function.hpp ++@@ -0,0 +1,220 @@ +++/* +++ +++Copyright (c) 2017, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#ifndef SIMULATOR_FUNCTION_HPP_INCLUDED +++#define SIMULATOR_FUNCTION_HPP_INCLUDED +++ +++#include +++#include // for allocator_traits +++ +++#include "simulator/mallocator.hpp" +++ +++namespace sim { +++namespace aux { +++ +++ template +++ T exchange_(T& var, U&& new_val) +++ { +++ T temp = std::move(var); +++ var = std::forward(new_val); +++ return temp; +++ } +++ +++ template +++ T* allocate_handler(Fun h) +++ { +++ using alloc = typename boost::asio::associated_allocator::type>::type; +++ using our_alloc = typename std::allocator_traits::template rebind_alloc; +++ our_alloc al(boost::asio::get_associated_allocator(h)); +++ void* ptr = al.allocate(1); +++ if (ptr == nullptr) throw std::bad_alloc(); +++ try { +++ return new (ptr) T(std::move(h)); +++ } +++ catch (...) { +++ al.deallocate(reinterpret_cast(ptr), 1); +++ throw; +++ } +++ } +++ +++ // this is a std::function-like class that supports move-only function +++ // objects +++ template +++ struct callable +++ { +++ using call_fun_t = R (*)(void*, A&&...); +++ using deallocate_fun_t = void (*)(void*); +++ call_fun_t call_fun; +++ deallocate_fun_t deallocate_fun; +++ }; +++ +++ template +++ R call_impl(void* mem, A&&... a); +++ +++ template +++ void dealloc_impl(void* mem); +++ +++ template +++ struct function_impl : callable +++ { +++ function_impl(Handler h) +++ : handler(std::move(h)) +++ { +++ this->call_fun = call_impl; +++ this->deallocate_fun = dealloc_impl; +++ } +++ Handler handler; +++ }; +++ +++ template +++ R call_impl(void* mem, A&&... a) +++ { +++ auto* obj = static_cast*>(mem); +++ Handler handler = std::move(obj->handler); +++ +++ obj->~function_impl(); +++ using alloc = typename boost::asio::associated_allocator::type; +++ using our_alloc = typename std::allocator_traits:: +++ template rebind_alloc::type>; +++ our_alloc al(boost::asio::get_associated_allocator(handler)); +++ al.deallocate(obj, 1); +++ +++ return handler(std::forward(a)...); +++ } +++ +++ template +++ void dealloc_impl(void* mem) +++ { +++ auto* obj = static_cast*>(mem); +++ Handler h = std::move(obj->handler); +++ +++ obj->~function_impl(); +++ using alloc = typename boost::asio::associated_allocator::type; +++ using our_alloc = typename std::allocator_traits:: +++ template rebind_alloc::type>; +++ our_alloc al(boost::asio::get_associated_allocator(h)); +++ al.deallocate(obj, 1); +++ } +++ +++ template +++ struct function; +++ +++ template +++ struct function +++ { +++ using result_type = R; +++ +++ using allocator_type = aux::mallocator; +++ allocator_type get_allocator() const { return allocator_type{}; } +++ +++ template +++ function(C c) +++ : m_callable(allocate_handler>(std::move(c))) +++ {} +++ function(function&& other) noexcept +++ : m_callable(exchange_(other.m_callable, nullptr)) +++ {} +++ function& operator=(function&& other) noexcept +++ { +++ if (&other == this) return *this; +++ clear(); +++ m_callable = exchange_(other.m_callable, nullptr); +++ return *this; +++ } +++ +++ ~function() { clear(); } +++ +++ // boost.asio requires handlers to be copy-constructible, but it will move +++ // them, if they're movable. So we trick asio into accepting this handler. +++ // If it attempts to copy, it will cause a link error +++ function(function const&) { assert(false && "functions should not be copied"); } +++ function& operator=(function const&) = delete; +++ +++ function() = default; +++ explicit operator bool() const { return m_callable != nullptr; } +++ function& operator=(std::nullptr_t) { clear(); return *this; } +++ void clear() +++ { +++ if (m_callable == nullptr) return; +++ auto fun = m_callable->deallocate_fun; +++ fun(m_callable); +++ m_callable = nullptr; +++ } +++ template +++ R operator()(Args&&... a) +++ { +++ assert(m_callable); +++ auto fun = m_callable->call_fun; +++ return fun(exchange_(m_callable, nullptr), std::forward(a)...); +++ } +++ private: +++ callable* m_callable = nullptr; +++ }; +++ +++ // index sequence, to unpack tuple +++ template struct seq {}; +++ template struct gens : gens {}; +++ template struct gens<0, S...> { using type = seq; }; +++ +++ // a binder for move-only types, and movable arguments. It's not a general +++ // binder as it doesn't support partial application, it just binds all +++ // arguments and ignores any arguments passed to the call +++ template +++ struct move_binder +++ { +++ move_binder(Callable c, A&&... a) +++ : m_args(std::move(a)...) +++ , m_callable(std::move(c)) +++ {} +++ +++ move_binder(move_binder const&) = delete; +++ move_binder& operator=(move_binder const&) = delete; +++ +++ move_binder(move_binder&&) = default; +++ move_binder& operator=(move_binder&&) = default; +++ +++ // ignore any arguments passed in. This is used to ignore an error_code +++ // argument for instance +++ template +++ R operator()(Args...) +++ { +++ return call(typename gens::type()); +++ } +++ +++ private: +++ +++ template +++ R call(seq) +++ { +++ return m_callable(std::move(std::get(m_args))...); +++ } +++ std::tuple m_args; +++ Callable m_callable; +++ }; +++ +++ template +++ move_binder move_bind(C c, A&&... a) +++ { +++ return move_binder(std::move(c), std::forward(a)...); +++ } +++ +++} +++} +++ +++#endif +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/handler_allocator.hpp ++@@ -0,0 +1,81 @@ +++/* +++ +++Copyright (c) 2015, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#ifndef HANDLER_ALLOCATOR_HPP_INCLUDED +++#define HANDLER_ALLOCATOR_HPP_INCLUDED +++ +++namespace sim +++{ +++namespace aux +++{ +++ +++template +++struct malloc_allocator +++{ +++ using value_type = T; +++ using size_type = std::size_t; +++ +++ friend bool operator==(malloc_allocator, malloc_allocator) { return true; } +++ friend bool operator!=(malloc_allocator, malloc_allocator) { return false; } +++ +++ template +++ struct rebind { using other = malloc_allocator; }; +++ +++ malloc_allocator() = default; +++ template +++ malloc_allocator(malloc_allocator const&) {} +++ +++ T* allocate(std::size_t size) { return static_cast(std::malloc(size * sizeof(T))); } +++ void deallocate(T* pointer, std::size_t) { std::free(pointer); } +++ using is_always_equal = std::true_type; +++}; +++ +++// this is a handler wrapper that customizes the asio handler allocator to use +++// malloc instead of new. The purpose is to distinguish allocations that are +++// internal to the simulator and allocations part of the program under test. +++template +++struct malloc_wrapper +++{ +++ malloc_wrapper(Handler h) : m_handler(std::move(h)) {} +++ +++ template +++ void operator()(Args&&... a) +++ { +++ m_handler(std::forward(a)...); +++ } +++ +++ using allocator_type = malloc_allocator>; +++ +++ allocator_type get_allocator() const noexcept +++ { return allocator_type{}; } +++ +++private: +++ Handler m_handler; +++}; +++ +++template +++malloc_wrapper make_malloc(T h) +++{ +++ return malloc_wrapper(std::move(h)); +++} +++ +++} +++} +++ +++#endif +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/http_proxy.hpp ++@@ -0,0 +1,103 @@ +++/* +++ +++Copyright (c) 2015, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#ifndef HTTP_PROXY_HPP_INCLUDED +++#define HTTP_PROXY_HPP_INCLUDED +++ +++#include "simulator/simulator.hpp" +++ +++#ifdef _MSC_VER +++#pragma warning(push) +++// warning C4251: X: class Y needs to have dll-interface to be used by clients of struct +++#pragma warning( disable : 4251) +++#endif +++ +++namespace sim +++{ +++ struct http_request; +++ +++// This is a very simple http proxy that only supports a single +++// concurrent connection +++struct SIMULATOR_DECL http_proxy +++{ +++ http_proxy(asio::io_context& ios, unsigned short listen_port); +++ +++ void stop(); +++ +++private: +++ +++ void on_accept(boost::system::error_code const& ec); +++ void on_read_request(boost::system::error_code const& ec, size_t bytes_transferred); +++ +++ void forward_request(http_request const& req); +++ void open_forward_connection(const asio::ip::tcp::endpoint& target); +++ void on_connected(boost::system::error_code const& ec); +++ +++ void on_domain_lookup(boost::system::error_code const& ec +++ , const asio::ip::tcp::resolver::results_type ips); +++ +++ void write_server_send_buffer(); +++ void on_server_write(boost::system::error_code const& ec, size_t bytes_transferred); +++ +++ void on_server_receive(boost::system::error_code const& ec +++ , std::size_t bytes_transferred); +++ void on_server_forward(boost::system::error_code const& ec, size_t bytes_transferred); +++ +++ void error(int code, char const* message); +++ void close_connection(); +++ +++ asio::ip::tcp::resolver m_resolver; +++ asio::ip::tcp::acceptor m_listen_socket; +++ +++ // this is the client connection, i.e. the client connecting to us, sending +++ // HTTP requests that we forward +++ asio::ip::tcp::socket m_client_connection; +++ // client endpoint +++ asio::ip::tcp::endpoint m_ep; +++ +++ // this is the connection to the server the client's requests are forwarded +++ // to +++ asio::ip::tcp::socket m_server_connection; +++ // true while there is an outstanding write operation to the server +++ bool m_writing_to_server; +++ +++ // receive buffer for requests from the client. i.e. client -> proxy (us) -> server +++ char m_client_in_buffer[65536]; +++ // buffer size +++ int m_num_client_in_bytes; +++ +++ char m_server_out_buffer[65536]; +++ int m_num_server_out_bytes; +++ +++ // receive buffer for incoming responses, i.e. server -> proxy (us) -> client +++ char m_in_buffer[65536]; +++ // buffer size +++ int m_num_in_bytes; +++ +++ // set to true when shutting down +++ bool m_close; +++}; +++ +++} +++ +++#ifdef _MSC_VER +++#pragma warning(pop) +++#endif +++ +++#endif +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/http_server.hpp ++@@ -0,0 +1,134 @@ +++/* +++ +++Copyright (c) 2015, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#ifndef HTTP_SERVER_HPP_INCLUDED +++#define HTTP_SERVER_HPP_INCLUDED +++ +++#include "simulator/simulator.hpp" +++#include +++ +++#ifdef _MSC_VER +++#pragma warning(push) +++// warning C4251: X: class Y needs to have dll-interface to be used by clients of struct +++#pragma warning( disable : 4251) +++#endif +++ +++namespace sim +++{ +++ std::string SIMULATOR_DECL trim(std::string s); +++ +++ std::string SIMULATOR_DECL lower_case(std::string s); +++ +++ std::string SIMULATOR_DECL normalize(const std::string& s); +++ +++ // returns the index to the last byte of the request, or -1 if the buffer +++ // does not contain a full http request +++ int SIMULATOR_DECL find_request_len(char const* buf, int len); +++ +++ struct http_request +++ { +++ std::string method; +++ std::string req; +++ std::string path; +++ std::map headers; +++ }; +++ +++ http_request parse_request(char const* start, int len); +++ +++ // builds an HTTP response buffer +++ std::string SIMULATOR_DECL send_response(int code, char const* status_message +++ , int len = 0, char const** extra_header = NULL); +++ +++// This is a very simple http server that only supports a single concurrent +++// connection +++struct SIMULATOR_DECL http_server +++{ +++ enum flags_t +++ { +++ keep_alive = 1, +++ // behave like an HTTP/1.0 server: respond with an HTTP/1.0 status line +++ // and close the connection after each response. HTTP/1.0 has no +++ // persistent connections, and the Connection header is an HTTP/1.1 +++ // mechanism, so no "Connection: close" is sent -- the client must detect +++ // the close from the protocol version (and the socket closing). +++ http_1_0 = 2 +++ }; +++ +++ http_server(asio::io_context& ios, unsigned short listen_port +++ , int flags = http_server::keep_alive); +++ +++ void stop(); +++ +++ using handler_t = std::function&)>; +++ using generator_t = std::function; +++ +++ void register_handler(std::string const& path, handler_t h); +++ void register_content(std::string const& path +++ , std::int64_t const size, generator_t gen); +++ void register_redirect(std::string const& path, std::string const& target); +++ void register_stall_handler(std::string const& path); +++ +++ // the number of TCP connections that have been accepted so far +++ int accepted_connections() const { return m_accepted_connections; } +++ +++private: +++ +++ void on_accept(boost::system::error_code const& ec); +++ void read(); +++ void on_read(boost::system::error_code const& ec, size_t bytes_transferred); +++ void on_write(boost::system::error_code const& ec, size_t bytes_transferred +++ , bool close); +++ void close_connection(); +++ +++ asio::io_context& m_ios; +++ +++ asio::ip::tcp::acceptor m_listen_socket; +++ +++ asio::ip::tcp::socket m_connection; +++ asio::ip::tcp::endpoint m_ep; +++ +++ std::unordered_map m_handlers; +++ std::set m_stall_handlers; +++ +++ // read buffer, we receive bytes into this buffer for the connection +++ std::string m_recv_buffer; +++ +++ // the number of bytes of m_recv_buffer that we've actually read data into. +++ // The remaining is uninitialized, possibly being read into in an async call +++ int m_bytes_used; +++ +++ std::string m_send_buffer; +++ +++ // set to true when shutting down +++ bool m_close; +++ +++ int m_flags; +++ +++ // counts the number of accepted TCP connections +++ int m_accepted_connections = 0; +++}; +++ +++} +++ +++#ifdef _MSC_VER +++#pragma warning(pop) +++#endif +++ +++#endif +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/mallocator.hpp ++@@ -0,0 +1,69 @@ +++/* +++ +++Copyright (c) 2017, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#ifndef SIMULATOR_MALLOCATOR_HPP_INCLUDED +++#define SIMULATOR_MALLOCATOR_HPP_INCLUDED +++ +++namespace sim +++{ +++namespace aux +++{ +++ struct channel; +++ struct packet; +++ struct pcap; +++ +++ template +++ struct mallocator +++ { +++ template +++ friend struct mallocator; +++ +++ using value_type = T; +++ using pointer = T*; +++ using const_pointer = T const*; +++ using reference = T&; +++ using const_reference = T const&; +++ using size_type = std::size_t; +++ using difference_type = std::ptrdiff_t; +++ +++ template +++ struct rebind { +++ using other = mallocator; +++ }; +++ +++ mallocator() = default; +++ template +++ mallocator(mallocator const&) {} +++ +++ T* allocate(std::size_t size) +++ { return reinterpret_cast(std::malloc(size * sizeof(T))); } +++ +++ void deallocate(T* ptr) { std::free(ptr); } +++ void deallocate(T* ptr, std::size_t) { std::free(ptr); } +++ +++ void destroy(pointer p) { p->~T(); } +++ void construct(pointer p, T&& value) { new (p) T(std::move(value)); } +++ +++ bool operator==(mallocator const&) const { return true; } +++ bool operator!=(mallocator const&) const { return false; } +++ }; +++} // aux +++} // sim +++ +++#endif // SIMULATOR_MALLOCATOR_HPP_INCLUDED +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/nat.hpp ++@@ -0,0 +1,51 @@ +++/* +++ +++Copyright (c) 2018, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#ifndef NAT_HPP_INCLUDED +++#define NAT_HPP_INCLUDED +++ +++#include "simulator/sink.hpp" +++#include "simulator/simulator.hpp" +++#include +++ +++namespace sim { +++ +++namespace aux { +++ struct packet; +++} +++ +++ struct SIMULATOR_DECL nat : sink +++ { +++ nat(asio::ip::address external_addr); +++ ~nat() = default; +++ +++ void incoming_packet(aux::packet p) override; +++ +++ // used for visualization +++ std::string label() const override; +++ +++ private: +++ +++ asio::ip::address m_external_addr; +++ }; +++ +++} // sim +++ +++#endif +++ +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/noexcept_movable.hpp ++@@ -0,0 +1,56 @@ +++/* +++ +++Copyright (c) 2017, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#ifndef NOEXCEPT_MOVABLE_HPP_INCLUDED +++#define NOEXCEPT_MOVABLE_HPP_INCLUDED +++ +++namespace sim { +++namespace aux { +++ +++ template +++ struct noexcept_movable : T +++ { +++ noexcept_movable() noexcept {} +++ noexcept_movable(noexcept_movable&& rhs) noexcept +++ : T(std::forward(rhs)) +++ {} +++ noexcept_movable(noexcept_movable const& rhs) +++ : T(static_cast(rhs)) +++ {} +++ noexcept_movable(T&& rhs) noexcept : T(std::forward(rhs)) {} // NOLINT +++ noexcept_movable(T const& rhs) : T(rhs) {} // NOLINT +++ noexcept_movable& operator=(noexcept_movable&& rhs) noexcept +++ { +++ this->T::operator=(std::forward(rhs)); +++ return *this; +++ } +++ noexcept_movable& operator=(noexcept_movable const& rhs) +++ { +++ this->T::operator=(rhs); +++ return *this; +++ } +++ +++ using T::T; +++ using T::operator=; +++ }; +++ +++} +++} +++ +++#endif +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/packet.hpp ++@@ -0,0 +1,94 @@ +++/* +++ +++Copyright (c) 2015, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#ifndef PACKET_HPP_INCLUDED +++#define PACKET_HPP_INCLUDED +++ +++#include "simulator/config.hpp" +++#include "simulator/simulator.hpp" // for route, endpoint +++ +++namespace sim { namespace aux { +++ +++ struct channel; +++ +++ struct packet +++ { +++ packet() = default; +++ +++ // this is move-only +++ packet(packet const&) = delete; +++ packet& operator=(packet const&) = delete; +++ packet(packet&&) = default; +++ packet& operator=(packet&&) = default; +++ +++ // to keep things simple, don't drop ACKs or errors +++ bool ok_to_drop() const +++ { +++ return type != type_t::syn_ack +++ && type != type_t::ack +++ && type != type_t::error; +++ } +++ +++ enum class type_t +++ { +++ uninitialized, // invalid type (used for debugging) +++ syn, // TCP connect +++ syn_ack, // TCP connection accepted +++ ack, // the seq_nr is interpreted as "we received this" +++ error, // the error_code (ec) is set +++ payload // the buffer is filled +++ }; +++ +++ type_t type = type_t::uninitialized; +++ +++ boost::system::error_code ec; +++ +++ // actual payload +++ std::vector buffer; +++ +++ // used for UDP packets +++ asio::ip::udp::endpoint from; +++ +++ // the number of bytes of overhead for this packet. The total packet +++ // size is the number of bytes in the buffer + this number +++ int overhead = 20; +++ +++ // each hop in the route will pop itself off and forward the packet to +++ // the next hop +++ route hops; +++ +++ // for SYN packets, this is set to the channel we're trying to +++ // establish +++ std::shared_ptr channel; +++ +++ // sequence number of this packet (used for debugging) +++ std::uint64_t seq_nr = 0; +++ +++ // the number of (payload) bytes sent over this channel so far. This is +++ // meant to map to the TCP sequence number +++ std::uint32_t byte_counter = 0; +++ +++ // this function must be called with this packet in case the packet is +++ // dropped. +++ aux::function drop_fun; +++ }; +++ +++}} // sim +++ +++#endif +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/pcap.hpp ++@@ -0,0 +1,42 @@ +++/* +++ +++Copyright (c) 2017, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#ifndef PCAP_HPP_INCLUDED +++#define PCAP_HPP_INCLUDED +++ +++#include +++#include "simulator/simulator.hpp" // for endpoint +++ +++namespace sim { namespace aux +++{ +++ struct packet; +++ +++ struct pcap +++ { +++ pcap(char const* filename); +++ void log_tcp(packet const& p, asio::ip::tcp::endpoint src +++ , asio::ip::tcp::endpoint dst); +++ void log_udp(packet const& p, asio::ip::udp::endpoint src +++ , asio::ip::udp::endpoint dst); +++ private: +++ std::fstream m_file; +++ }; +++}} +++ +++#endif +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/pop_warnings.hpp ++@@ -0,0 +1,44 @@ +++/* +++ +++Copyright (c) 2015, Arvid Norberg +++All rights reserved. +++ +++Redistribution and use in source and binary forms, with or without +++modification, are permitted provided that the following conditions +++are met: +++ +++ * Redistributions of source code must retain the above copyright +++ notice, this list of conditions and the following disclaimer. +++ * Redistributions in binary form must reproduce the above copyright +++ notice, this list of conditions and the following disclaimer in +++ the documentation and/or other materials provided with the distribution. +++ * Neither the name of the author nor the names of its +++ contributors may be used to endorse or promote products derived +++ from this software without specific prior written permission. +++ +++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +++AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +++IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +++ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +++LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +++CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +++SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +++INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +++CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +++ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +++POSSIBILITY OF SUCH DAMAGE. +++ +++*/ +++ +++#ifdef _MSC_VER +++#pragma warning(pop) +++#endif +++ +++#ifdef __clang__ +++#pragma clang diagnostic pop +++#endif +++ +++#ifdef __GNUC__ +++#pragma GCC diagnostic pop +++#endif +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/push_warnings.hpp ++@@ -0,0 +1,89 @@ +++/* +++ +++Copyright (c) 2015, Arvid Norberg +++All rights reserved. +++ +++Redistribution and use in source and binary forms, with or without +++modification, are permitted provided that the following conditions +++are met: +++ +++ * Redistributions of source code must retain the above copyright +++ notice, this list of conditions and the following disclaimer. +++ * Redistributions in binary form must reproduce the above copyright +++ notice, this list of conditions and the following disclaimer in +++ the documentation and/or other materials provided with the distribution. +++ * Neither the name of the author nor the names of its +++ contributors may be used to endorse or promote products derived +++ from this software without specific prior written permission. +++ +++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +++AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +++IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +++ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +++LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +++CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +++SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +++INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +++CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +++ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +++POSSIBILITY OF SUCH DAMAGE. +++ +++*/ +++ +++#ifdef __GNUC__ +++#pragma GCC diagnostic push +++#pragma GCC diagnostic ignored "-Wall" +++#pragma GCC diagnostic ignored "-Wsign-conversion" +++#pragma GCC diagnostic ignored "-Wconversion" +++#pragma GCC diagnostic ignored "-Wswitch-enum" +++#pragma GCC diagnostic ignored "-Wold-style-cast" +++#pragma GCC diagnostic ignored "-Wundef" +++#pragma GCC diagnostic ignored "-Wmissing-noreturn" +++#pragma GCC diagnostic ignored "-Wdeprecated" +++#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +++#pragma GCC diagnostic ignored "-Wshadow" +++#pragma GCC diagnostic ignored "-Wunused-variable" +++#pragma GCC diagnostic ignored "-Wpedantic" +++#if __GNUC__ >= 6 +++#pragma GCC diagnostic ignored "-Wshift-overflow" +++#pragma GCC diagnostic ignored "-Wshift-count-overflow" +++#pragma GCC diagnostic ignored "-Wshift-count-negative" +++#endif +++#endif +++ +++#ifdef __clang__ +++#pragma clang diagnostic push +++#pragma clang diagnostic ignored "-Wall" +++#pragma clang diagnostic ignored "-Weverything" +++#pragma clang diagnostic ignored "-Wsign-conversion" +++#pragma clang diagnostic ignored "-Wconversion" +++#pragma clang diagnostic ignored "-Wswitch-enum" +++#pragma clang diagnostic ignored "-Wcovered-switch-default" +++#pragma clang diagnostic ignored "-Wold-style-cast" +++#pragma clang diagnostic ignored "-Wundef" +++#pragma clang diagnostic ignored "-Wweak-vtables" +++#pragma clang diagnostic ignored "-Wmissing-noreturn" +++#pragma clang diagnostic ignored "-Wdeprecated" +++#pragma clang diagnostic ignored "-Wdeprecated-declarations" +++#pragma clang diagnostic ignored "-Wcast-align" +++#pragma clang diagnostic ignored "-Wweak-vtable" +++#pragma clang diagnostic ignored "-Wundef" +++#pragma clang diagnostic ignored "-Wshadow" +++#pragma clang diagnostic ignored "-Wimplicit-fallthrough" +++#pragma clang diagnostic ignored "-Wc++11-long-long" +++#pragma clang diagnostic ignored "-Wc++11-extensions" +++#pragma clang diagnostic ignored "-Wextra-semi" +++#pragma clang diagnostic ignored "-Wunused-parameter" +++#pragma clang diagnostic ignored "-Wreserved-id-macro" +++#pragma clang diagnostic ignored "-Wunused-local-typedef" +++#pragma clang diagnostic ignored "-Wgnu-folding-constant" +++#pragma clang diagnostic ignored "-Wdouble-promotion" +++#pragma clang diagnostic ignored "-Wfloat-equal" +++#endif +++ +++#ifdef _MSC_VER +++#pragma warning(push, 1) +++// warning C4005: macro redefinition +++#pragma warning( disable : 4005 ) +++#endif +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/queue.hpp ++@@ -0,0 +1,100 @@ +++/* +++ +++Copyright (c) 2015, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#ifndef QUEUE_HPP_INCLUDED +++#define QUEUE_HPP_INCLUDED +++ +++#include "simulator/simulator.hpp" +++#include "simulator/packet.hpp" +++#include "simulator/mallocator.hpp" +++ +++#ifdef _MSC_VER +++#pragma warning(push) +++// warning C4251: X: class Y needs to have dll-interface to be used by clients of struct +++#pragma warning( disable : 4251) +++#endif +++ +++namespace sim { +++ +++ struct timed_packet +++ { +++ timed_packet(chrono::high_resolution_clock::time_point t, aux::packet p) +++ : ts(t), pkt(std::move(p)) +++ {} +++ timed_packet(timed_packet&&) = default; +++ timed_packet& operator=(timed_packet&&) = default; +++ timed_packet(timed_packet const&) = delete; +++ timed_packet& operator=(timed_packet const&) = delete; +++ chrono::high_resolution_clock::time_point ts; +++ aux::packet pkt; +++ }; +++ +++ // this is a queue. It can be configured to contrain +++ struct SIMULATOR_DECL queue : sink +++ { +++ queue(asio::io_context& ios, int bandwidth +++ , chrono::high_resolution_clock::duration propagation_delay +++ , int max_queue_size, std::string name = "queue"); +++ +++ virtual void incoming_packet(aux::packet p) override final; +++ +++ virtual std::string label() const override final; +++ +++ queue(queue const&) = delete; +++ queue& operator=(queue const&) = delete; +++ +++ queue(queue&&) = default; +++ queue& operator=(queue&&) = delete; +++ +++ private: +++ +++ void begin_send_next_packet(); +++ void next_packet_sent(); +++ +++ // the queue can't hold more than this number of bytes. Once it's full, +++ // any new packets arriving will be dropped (tail drop) +++ int m_max_queue_size; +++ +++ // the amount of time it takes to forward a packet. Every packet is +++ // delayed by at least this much before being forwarded +++ chrono::high_resolution_clock::duration m_forwarding_latency; +++ +++ // the number of bytes per second that can be sent. This includes the +++ // packet overhead +++ int m_bandwidth; +++ +++ // the number of bytes currently in the packet queue +++ int m_queue_size; +++ +++ std::string m_node_name; +++ +++ // this is the queue of packets and the time each packet was enqueued +++ std::deque> m_queue; +++ asio::high_resolution_timer m_forward_timer; +++ +++ chrono::high_resolution_clock::time_point m_last_forward; +++ }; +++ +++} +++ +++#ifdef _MSC_VER +++#pragma warning(pop) +++#endif +++ +++#endif +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/simulator.hpp ++@@ -0,0 +1,1486 @@ +++/* +++ +++Copyright (c) 2015, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#ifndef SIMULATOR_HPP_INCLUDED +++#define SIMULATOR_HPP_INCLUDED +++ +++#include "simulator/push_warnings.hpp" +++ +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++ +++#include +++#include +++#include +++#include +++ +++// only for boost::beast::role_type, used by the teardown()/async_teardown() +++// overloads below (customization points boost::beast::websocket::stream +++// needs for any non-boost::asio socket type it's instantiated over); this +++// is the single lightweight header that defines it, not a dependency on +++// beast's websocket/http machinery. +++#include +++ +++#include +++ +++#include "simulator/pop_warnings.hpp" +++ +++#include "simulator/chrono.hpp" +++#include "simulator/sink_forwarder.hpp" +++#include "simulator/function.hpp" +++#include "simulator/noexcept_movable.hpp" +++#include "simulator/mallocator.hpp" +++ +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++ +++#ifndef IP_DONTFRAGMENT +++#define IP_DONTFRAGMENT 1 +++#endif +++ +++namespace sim +++{ +++ namespace aux +++ { +++ struct channel; +++ struct packet; +++ struct pcap; +++ +++ // this is outgoing NIC bandwidth +++ constexpr int nic_bandwidth = 100000000; // 100 MB/s +++ } +++ +++ // this represents a network route (a series of sinks to pass a packet +++ // through) +++ struct SIMULATOR_DECL route +++ { +++ friend route operator+(route lhs, route rhs) +++ { return std::move(lhs.append(std::move(rhs))); } +++ +++ std::shared_ptr next_hop() const { return hops.front(); } +++ std::shared_ptr pop_front() +++ { +++ if (hops.empty()) return std::shared_ptr(); +++ std::shared_ptr ret(std::move(hops.front())); +++ hops.erase(hops.begin()); +++ return ret; +++ } +++ void replace_last(std::shared_ptr s) { hops.back() = std::move(s); } +++ void prepend(route const& r) +++ { hops.insert(hops.begin(), r.hops.begin(), r.hops.end()); } +++ void prepend(std::shared_ptr s) { hops.insert(hops.begin(), std::move(s)); } +++ route& append(route const& r) +++ { hops.insert(hops.end(), r.hops.begin(), r.hops.end()); return *this; } +++ route& append(std::shared_ptr s) { hops.push_back(std::move(s)); return *this; } +++ bool empty() const { return hops.empty(); } +++ std::shared_ptr last() const +++ { return hops.back(); } +++ +++ private: +++ std::deque, aux::mallocator>> hops; +++ }; +++ +++ void forward_packet(aux::packet p); +++ +++ struct simulation; +++ struct configuration; +++ struct queue; +++ +++ namespace asio +++ { +++ +++ using boost::asio::buffer_size; +++ using boost::asio::const_buffer; +++ using boost::asio::mutable_buffer; +++ using boost::asio::buffer; +++ +++ // brought in here (rather than relying on ADL at each call site below) +++ // so buffer sequence types whose associated namespaces don't include +++ // boost::asio (e.g. boost::beast's internal buffer sequence types) are +++ // still found; the "using" also participates in ADL's overload set at +++ // the point of an unqualified call, so this doesn't shadow lookups for +++ // types that do live in boost::asio. +++ using boost::asio::buffer_sequence_begin; +++ using boost::asio::buffer_sequence_end; +++ +++ using boost::asio::post; +++ using boost::asio::dispatch; +++ using boost::asio::defer; +++ +++ struct io_context; +++ +++ struct io_executor +++ { +++ io_executor(io_context& ctx) : m_ctx(&ctx) {} +++ io_context& context() const { return *m_ctx; } +++ +++ template +++ void dispatch(Handler handler, Allocator const& a) const; +++ +++ template +++ void post(Handler handler, Allocator const& a) const; +++ +++ template +++ void defer(Handler handler, Allocator const& a) const; +++ +++ void on_work_finished() const {} +++ void on_work_started() const {} +++ +++ bool running_in_this_thread() const { return true; } +++ +++ friend bool operator==(io_executor const& lhs, io_executor const& rhs) +++ { return lhs.m_ctx == rhs.m_ctx; } +++ +++ friend bool operator!=(io_executor const& lhs, io_executor const& rhs) +++ { return lhs.m_ctx != rhs.m_ctx; } +++ +++ private: +++ io_context* m_ctx; +++ }; +++ +++ struct SIMULATOR_DECL high_resolution_timer +++ { +++ friend struct sim::simulation; +++ +++ using time_type = chrono::high_resolution_clock::time_point; +++ using duration_type = chrono::high_resolution_clock::duration; +++ +++ using executor_type = io_executor; +++ executor_type get_executor(); +++ +++ explicit high_resolution_timer(io_context& io_context); +++ high_resolution_timer(io_context& io_context, +++ const time_type& expiry_time); +++ high_resolution_timer(io_context& io_context, +++ const duration_type& expiry_time); +++ high_resolution_timer(high_resolution_timer&&) noexcept = default; +++ high_resolution_timer& operator=(high_resolution_timer&&) noexcept = default; +++ ~high_resolution_timer(); +++ +++ std::size_t cancel(); +++ std::size_t cancel_one(); +++ +++ time_type expiry() const; +++ std::size_t expires_at(const time_type& expiry_time); +++ std::size_t expires_after(const duration_type& expiry_time); +++ +++ void wait(); +++ void wait(boost::system::error_code& ec); +++ +++ void async_wait(aux::function handler); +++ +++ private: +++ +++ void fire(boost::system::error_code ec); +++ +++ time_type m_expiration_time; +++ aux::function m_handler; +++ io_context* m_io_service; +++ bool m_expired; +++ }; +++ +++ using waitable_timer = high_resolution_timer; +++ +++ namespace error = boost::asio::error; +++ +++ template +++ struct socket_base +++ { +++ socket_base(io_context& ios) : m_io_service(ios) {} +++ socket_base(socket_base&& s) = default; +++ +++ enum wait_type_t +++ { +++ wait_read, wait_write, wait_error +++ }; +++ +++ // alias matching the name used by boost.asio's basic_socket::wait_type, +++ // for code that's written against both APIs +++ using wait_type = wait_type_t; +++ +++ // io_control +++ using reuse_address = boost::asio::socket_base::reuse_address; +++ using executor_type = io_executor; +++ executor_type get_executor(); +++ +++ // socket options +++ using send_buffer_size = boost::asio::socket_base::send_buffer_size; +++ using receive_buffer_size = boost::asio::socket_base::receive_buffer_size; +++ +++ template +++ void set_option(Option const& opt, boost::system::error_code&) +++ { +++ Protocol const p = Protocol::v4(); +++ (void)p; +++#ifdef IP_DONTFRAG +++ if (opt.name(p) == IP_DONTFRAG) +++ m_dont_fragment = *reinterpret_cast(opt.data(p)) != 0; +++#endif +++#ifdef IP_DONTFRAGMENT +++ if (opt.name(p) == IP_DONTFRAGMENT) +++ m_dont_fragment = *reinterpret_cast(opt.data(p)) != 0; +++#endif +++#ifdef IP_MTU_DISCOVER +++ if (opt.name(p) == IP_MTU_DISCOVER) +++ m_dont_fragment = *reinterpret_cast(opt.data(p)) == IP_PMTUDISC_DO; +++#endif +++ } +++ +++ void set_option(receive_buffer_size const& op, boost::system::error_code&) +++ { +++ m_max_receive_queue_size = op.value(); +++ } +++ +++ void set_option(send_buffer_size const& op, boost::system::error_code&) +++ { +++ // this limit is specified in microseconds. Given the line rate of +++ // nic_bandwidth, this is the time it takes to send the specified number +++ // of bytes. +++ m_send_queue_time = chrono::microseconds(std::int64_t(double(op.value()) * 1000000.0 / double(aux::nic_bandwidth))); +++ } +++ +++ void set_option(reuse_address const&, boost::system::error_code&) +++ { +++ // TODO: implement +++ } +++ +++ typename Protocol::endpoint local_endpoint(boost::system::error_code& ec) const +++ { +++ if (!m_open) +++ { +++ ec = error::bad_descriptor; +++ return typename Protocol::endpoint{}; +++ } +++ +++ return m_user_bound_to; +++ } +++ +++ typename Protocol::endpoint local_endpoint() const +++ { +++ boost::system::error_code ec; +++ auto const ret = local_endpoint(ec); +++ if (ec) throw boost::system::system_error(ec); +++ return ret; +++ } +++ +++ typename Protocol::endpoint local_bound_to(boost::system::error_code& ec) const +++ { +++ if (!m_open) +++ { +++ ec = error::bad_descriptor; +++ return typename Protocol::endpoint{}; +++ } +++ +++ return m_bound_to; +++ } +++ +++ typename Protocol::endpoint local_bound_to() const +++ { +++ boost::system::error_code ec; +++ auto const ret = local_bound_to(ec); +++ if (ec) throw boost::system::system_error(ec); +++ return ret; +++ } +++ +++ template +++ void get_option(Option&, boost::system::error_code&) { } +++ +++ void get_option(receive_buffer_size& op, boost::system::error_code&) +++ { +++ op = m_max_receive_queue_size; +++ } +++ +++ template +++ void io_control(IoControl const&, boost::system::error_code&) { } +++ +++ template +++ void io_control(IoControl const&) {} +++ +++ void non_blocking(bool b, boost::system::error_code&) +++ { m_non_blocking = b; } +++ +++ void non_blocking(bool b) +++ { m_non_blocking = b; } +++ +++ bool is_open() const +++ { +++ return m_open; +++ } +++ +++ using message_flags = int; +++ +++ // internal interface +++ +++ route get_incoming_route(); +++ route get_outgoing_route(); +++ +++ protected: +++ +++ io_context& m_io_service; +++ +++ typename Protocol::endpoint m_bound_to; +++ +++ // this is the interface the user requested to bind to (in order to +++ // distinguish the concrete interface it was bound to and INADDR_ANY if +++ // that was requested). We keep this separately to return it as the local +++ // endpoint +++ typename Protocol::endpoint m_user_bound_to; +++ +++ // this is an object implementing the sink interface, forwarding +++ // packets to this socket. If this socket is destructed, this forwarder +++ // is redirected to just drop packets. This is necessary since sinks +++ // must be held by shared_ptr, and socket objects aren't. +++ std::shared_ptr m_forwarder; +++ +++ // whether the socket is open or not +++ bool m_open = false; +++ +++ // true if the socket is set to non-blocking mode +++ bool m_non_blocking = false; +++ +++ // when true, the MTU limit is in effect +++ bool m_dont_fragment = false; +++ +++ // the max size of the incoming queue. This is to emulate the send and +++ // receive buffer. This should also depend on the bandwidth, to not +++ // make the queue size not grow too long in time. +++ int m_max_receive_queue_size = 64 * 1024; +++ +++ // the number of microseconds worth of send buffer this socket has, at +++ // NIC linerate. +++ chrono::microseconds m_send_queue_time{200000}; +++ }; +++ +++ namespace ip { +++ +++ using boost::asio::ip::address; +++ using boost::asio::ip::address_v4; +++ using boost::asio::ip::address_v6; +++ +++ using boost::asio::ip::make_address_v4; +++ using boost::asio::ip::make_address_v6; +++ using boost::asio::ip::make_address; +++ +++ using boost::asio::ip::make_network_v4; +++ +++ template +++ struct basic_endpoint : boost::asio::ip::basic_endpoint +++ { +++ basic_endpoint(ip::address const& addr, unsigned short port) +++ : boost::asio::ip::basic_endpoint(addr, port) {} +++ basic_endpoint() : boost::asio::ip::basic_endpoint() {} +++ }; +++ +++ template +++ struct basic_resolver_entry +++ { +++ using endpoint_type = typename Protocol::endpoint; +++ using protocol_type = Protocol; +++ +++ basic_resolver_entry() {} +++ basic_resolver_entry( +++ endpoint_type const& ep +++ , std::string const& host +++ , std::string const& service) +++ : m_endpoint(ep) +++ , m_host_name(host) +++ , m_service(service) +++ {} +++ +++ endpoint_type endpoint() const { return m_endpoint; } +++ std::string host_name() const { return m_host_name; } +++ operator endpoint_type() const { return m_endpoint; } +++ std::string service_name() const { return m_service; } +++ +++ private: +++ endpoint_type m_endpoint; +++ std::string m_host_name; +++ std::string m_service; +++ }; +++ +++ template +++ struct SIMULATOR_DECL basic_resolver +++ { +++ basic_resolver(io_context& ios); +++ +++ using protocol_type = Protocol; +++ using results_type = std::vector, aux::mallocator>>; +++ +++ void cancel(); +++ +++ void async_resolve(std::string hostname, char const* service +++ , aux::function handler); +++ +++ basic_resolver(basic_resolver&&) noexcept; +++ basic_resolver& operator=(basic_resolver&&) noexcept; +++ basic_resolver(basic_resolver const&) = delete; +++ basic_resolver& operator=(basic_resolver const&) = delete; +++ +++ //TODO: add remaining members +++ +++ private: +++ +++ void on_lookup(boost::system::error_code const& ec); +++ +++ struct result_t +++ { +++ chrono::high_resolution_clock::time_point completion_time; +++ boost::system::error_code err; +++ results_type ips; +++ aux::function handler; +++ +++ result_t( +++ chrono::high_resolution_clock::time_point ct +++ , boost::system::error_code e +++ , results_type ips_ +++ , aux::function h) +++ : completion_time(ct) +++ , err(e) +++ , ips(std::move(ips_)) +++ , handler(std::move(h)) +++ {} +++ +++ result_t(result_t&&) noexcept = default; +++ result_t& operator=(result_t&&) = default; +++ result_t(result_t const&) = delete; +++ result_t& operator=(result_t const&) = delete; +++ }; +++ +++ io_context* m_ios; +++ asio::high_resolution_timer m_timer; +++ using queue_t = aux::noexcept_movable>; +++ +++ queue_t m_queue; +++ }; +++ +++ struct SIMULATOR_DECL udp +++ { +++ static udp v4() { return udp(AF_INET); } +++ static udp v6() { return udp(AF_INET6); } +++ +++ using endpoint = basic_endpoint; +++ +++ struct SIMULATOR_DECL socket : socket_base, sink +++ { +++ using endpoint_type = ip::udp::endpoint; +++ using protocol_type = ip::udp; +++ using lowest_layer_type = socket; +++ +++ socket(io_context& ios); +++ ~socket() override; +++ +++ socket(socket const&) = delete; +++ socket& operator=(socket const&) = delete; +++ socket(socket&&); +++ +++ lowest_layer_type& lowest_layer() { return *this; } +++ +++ void bind(ip::udp::endpoint const& ep +++ , boost::system::error_code& ec); +++ void bind(ip::udp::endpoint const& ep); +++ +++ void close(); +++ void close(boost::system::error_code& ec); +++ +++ void cancel(boost::system::error_code& ec); +++ void cancel(); +++ +++ void open(udp protocol, boost::system::error_code& ec); +++ void open(udp protocol); +++ +++ template +++ std::size_t send_to(ConstBufferSequence const& bufs +++ , udp::endpoint const& destination +++ , socket_base::message_flags flags +++ , boost::system::error_code& ec) +++ { +++ std::vector b(buffer_sequence_begin(bufs) +++ , buffer_sequence_end(bufs)); +++ abort_send_handlers(); +++ return send_to_impl(b, destination, flags, ec); +++ } +++ +++ template +++ std::size_t send_to(ConstBufferSequence const& bufs +++ , udp::endpoint const& destination) +++ { +++ std::vector b(buffer_sequence_begin(bufs), buffer_sequence_end(bufs)); +++ abort_send_handlers(); +++ boost::system::error_code ec; +++ std::size_t ret = send_to_impl(b, destination, 0, ec); +++ if (ec) throw boost::system::system_error(ec); +++ return ret; +++ } +++ +++ void async_wait(socket_base::wait_type_t w +++ , aux::function handler); +++ +++ template +++ void async_receive(BufferSequence const& bufs +++ , aux::function handler) +++ { +++ std::vector b(buffer_sequence_begin(bufs), buffer_sequence_end(bufs)); +++ abort_recv_handlers(); +++ +++ async_receive_from_impl(b, nullptr, 0, std::move(handler)); +++ } +++ +++ template +++ void async_receive_from(BufferSequence const& bufs +++ , udp::endpoint& sender +++ , aux::function handler) +++ { +++ std::vector b(buffer_sequence_begin(bufs), buffer_sequence_end(bufs)); +++ abort_recv_handlers(); +++ +++ async_receive_from_impl(b, &sender, 0, std::move(handler)); +++ } +++ +++ template +++ void async_receive_from(BufferSequence const& bufs +++ , udp::endpoint& sender +++ , socket_base::message_flags flags +++ , aux::function handler) +++ { +++ std::vector b(buffer_sequence_begin(bufs), buffer_sequence_end(bufs)); +++ abort_recv_handlers(); +++ +++ async_receive_from_impl(b, &sender, flags, std::move(handler)); +++ } +++/* +++ void async_read_from(null_buffers const& +++ , aux::function handler) +++ { +++ abort_recv_handlers(); +++ async_read_some_null_buffers_impl(std::move(handler)); +++ } +++*/ +++ +++ template +++ std::size_t receive_from(BufferSequence const& bufs +++ , udp::endpoint& sender) +++ { +++ std::vector b(buffer_sequence_begin(bufs), buffer_sequence_end(bufs)); +++ assert(!b.empty()); +++ abort_recv_handlers(); +++ boost::system::error_code ec; +++ std::size_t ret = receive_from_impl(b, &sender, 0, ec); +++ if (ec) throw boost::system::system_error(ec); +++ return ret; +++ } +++ +++ template +++ std::size_t receive_from(BufferSequence const& bufs +++ , udp::endpoint& sender +++ , socket_base::message_flags) +++ { +++ std::vector b(buffer_sequence_begin(bufs), buffer_sequence_end(bufs)); +++ assert(!b.empty()); +++ abort_recv_handlers(); +++ boost::system::error_code ec; +++ std::size_t ret = receive_from_impl(b, &sender, 0, ec); +++ if (ec) throw boost::system::system_error(ec); +++ return ret; +++ } +++ +++ template +++ std::size_t receive_from(BufferSequence const& bufs +++ , udp::endpoint& sender +++ , socket_base::message_flags +++ , boost::system::error_code& ec) +++ { +++ std::vector b(buffer_sequence_begin(bufs), buffer_sequence_end(bufs)); +++ assert(!b.empty()); +++ abort_recv_handlers(); +++ return receive_from_impl(b, &sender, 0, ec); +++ } +++ +++ // TODO: support connect and remote_endpoint +++ +++ // internal interface +++ +++ // implements sink +++ virtual void incoming_packet(aux::packet p) override final; +++ virtual std::string label() const override final +++ { return m_bound_to.address().to_string(); } +++ +++ void async_receive_from_impl(std::vector const& bufs +++ , udp::endpoint* sender +++ , socket_base::message_flags flags +++ , aux::function handler); +++ +++ std::size_t receive_from_impl( +++ std::vector const& bufs +++ , udp::endpoint* sender +++ , socket_base::message_flags flags +++ , boost::system::error_code& ec); +++ +++ void async_wait_receive_impl( +++ udp::endpoint* sender +++ , aux::function handler); +++ +++ private: +++ +++ void maybe_wakeup_reader(); +++ void abort_send_handlers(); +++ void abort_recv_handlers(); +++ +++ std::size_t send_to_impl(std::vector const& b +++ , udp::endpoint const& dst, message_flags flags +++ , boost::system::error_code& ec); +++ +++ // this is the next time we'll have an opportunity to send another +++ // outgoing packet. This is used to implement the bandwidth constraints +++ // of channels. This may be in the past, in which case it's OK to send +++ // a packet immediately. +++ chrono::high_resolution_clock::time_point m_next_send; +++ +++ // while we're blocked in an async_write_some operation, this is the +++ // handler that should be called once we're done sending +++ aux::function +++ m_send_handler; +++ aux::function +++ m_wait_send_handler; +++ +++ // if we have an outstanding read on this socket, this is set to the +++ // handler. +++ aux::function +++ m_recv_handler; +++ aux::function +++ m_wait_recv_handler; +++ +++ // if we have an outstanding read operation, this is the buffer to +++ // receive into +++ std::vector m_recv_buffer; +++ +++ // if we have an outstanding receive operation, this may point to an +++ // endpoint to fill in the senders IP in +++ udp::endpoint* m_recv_sender; +++ +++ asio::high_resolution_timer m_recv_timer; +++ asio::high_resolution_timer m_send_timer; +++ +++ // this is the incoming queue of packets for each socket +++ std::list> m_incoming_queue; +++ +++ bool m_recv_null_buffers; +++ +++ // the number of bytes in the incoming packet queue +++ int m_queue_size; +++ +++ // our address family +++ bool m_is_v4; +++ }; +++ +++ using resolver = basic_resolver; +++ +++ int family() const { return m_family; } +++ +++ friend bool operator==(udp const& lhs, udp const& rhs) +++ { return lhs.m_family == rhs.m_family; } +++ +++ friend bool operator!=(udp const& lhs, udp const& rhs) +++ { return lhs.m_family != rhs.m_family; } +++ +++ private: +++ // Construct with a specific family. +++ explicit udp(int protocol_family) +++ : m_family(protocol_family) +++ {} +++ +++ int m_family; +++ +++ }; // udp +++ +++ struct SIMULATOR_DECL tcp +++ { +++ // temporary fix until the resolvers are implemented using our endpoint +++ tcp(boost::asio::ip::tcp p) : m_family(p.family()) {} +++ +++ static tcp v4() { return tcp(AF_INET); } +++ static tcp v6() { return tcp(AF_INET6); } +++ +++ int family() const { return m_family; } +++ +++ using endpoint = basic_endpoint; +++ +++ struct SIMULATOR_DECL socket : socket_base, sink +++ { +++ using endpoint_type = ip::tcp::endpoint; +++ using protocol_type = ip::tcp; +++ using lowest_layer_type = socket; +++ +++ explicit socket(io_context& ios); +++ socket(socket const&) = delete; +++ socket& operator=(socket const&) = delete; +++ socket(socket&&); +++ socket& operator=(socket&&); +++ +++ ~socket() override; +++ +++ void close(); +++ void close(boost::system::error_code& ec); +++ void open(tcp protocol, boost::system::error_code& ec); +++ void open(tcp protocol); +++ void bind(ip::tcp::endpoint const& ep +++ , boost::system::error_code& ec); +++ void bind(ip::tcp::endpoint const& ep); +++ tcp::endpoint remote_endpoint(boost::system::error_code& ec) const; +++ tcp::endpoint remote_endpoint() const; +++ +++ lowest_layer_type& lowest_layer() { return *this; } +++ +++ void async_connect(tcp::endpoint const& target +++ , aux::function h); +++ +++ template +++ void async_write_some(ConstBufferSequence const& bufs +++ , aux::function handler) +++ { +++ std::vector b(buffer_sequence_begin(bufs), buffer_sequence_end(bufs)); +++ abort_send_handlers(); +++ async_write_some_impl(b, std::move(handler)); +++ } +++ +++ void async_wait(socket_base::wait_type_t const w +++ , aux::function handler) +++ { +++ if (w == socket_base::wait_type_t::wait_write) +++ { +++ abort_send_handlers(); +++ async_wait_write_impl(std::move(handler)); +++ } +++ else if (w == socket_base::wait_type_t::wait_read) +++ { +++ abort_recv_handlers(); +++ async_wait_read_impl(std::move(handler)); +++ } +++ } +++ +++ template +++ std::size_t read_some(BufferSequence const& bufs +++ , boost::system::error_code& ec) +++ { +++ assert(m_non_blocking && "blocking operations not supported"); +++ std::vector b(buffer_sequence_begin(bufs), buffer_sequence_end(bufs)); +++ return read_some_impl(b, ec); +++ } +++ +++ template +++ std::size_t write_some(ConstBufferSequence const& bufs +++ , boost::system::error_code& ec) +++ { +++ assert(m_non_blocking && "blocking operations not supported"); +++ std::vector b(buffer_sequence_begin(bufs), buffer_sequence_end(bufs)); +++ return write_some_impl(b, ec); +++ } +++ +++ template +++ void async_read_some(BufferSequence const& bufs +++ , aux::function handler) +++ { +++ std::vector b(buffer_sequence_begin(bufs), buffer_sequence_end(bufs)); +++ abort_recv_handlers(); +++ +++ async_read_some_impl(b, std::move(handler)); +++ } +++ +++ std::size_t available(boost::system::error_code & ec) const; +++ std::size_t available() const; +++ +++ void cancel(boost::system::error_code& ec); +++ void cancel(); +++ +++ using socket_base::set_option; +++ using socket_base::get_option; +++ using socket_base::io_control; +++ +++ // private interface +++ +++ // implements sink +++ virtual void incoming_packet(aux::packet p) override; +++ virtual std::string label() const override final +++ { return m_bound_to.address().to_string(); } +++ +++ void internal_connect(tcp::endpoint const& bind_ip +++ , std::shared_ptr const& c +++ , boost::system::error_code& ec); +++ +++ void abort_send_handlers(); +++ void abort_recv_handlers(); +++ +++ virtual bool internal_is_listening(); +++ protected: +++ +++ void maybe_wakeup_reader(); +++ void maybe_wakeup_writer(); +++ +++ void async_write_some_impl(std::vector const& bufs +++ , aux::function handler); +++ void async_read_some_impl(std::vector const& bufs +++ , aux::function handler); +++ void async_wait_read_impl( +++ aux::function handler); +++ void async_wait_write_impl( +++ aux::function handler); +++ std::size_t write_some_impl(std::vector const& bufs +++ , boost::system::error_code& ec); +++ std::size_t read_some_impl(std::vector const& bufs +++ , boost::system::error_code& ec); +++ +++ void send_packet(aux::packet p); +++ +++ // called when a packet is dropped +++ void packet_dropped(aux::packet p); +++ +++ aux::function m_connect_handler; +++ +++ asio::high_resolution_timer m_connect_timer; +++ +++ // the tcp "packet size" (segment size) +++ // TODO: name this constant! +++ int m_mss = 1475; +++ +++ // while we're blocked in an async_write_some operation, this is the +++ // handler that should be called once we're done sending +++ aux::function m_send_handler; +++ aux::function m_wait_send_handler; +++ +++ std::vector m_send_buffer; +++ +++ // this is the incoming queue of packets for each socket +++ std::list> m_incoming_queue; +++ +++ // the number of bytes in the incoming packet queue +++ int m_queue_size = 0; +++ +++ // if we have an outstanding read on this socket, this is set to the +++ // handler. +++ aux::function m_recv_handler; +++ aux::function m_wait_recv_handler; +++ +++ // if we have an outstanding buffer to receive into, these are them +++ std::vector m_recv_buffer; +++ +++ asio::high_resolution_timer m_recv_timer; +++ +++ // our address family +++ bool m_is_v4 = true; +++ +++ // true if the currently outstanding read operation is for null_buffers +++ bool m_recv_null_buffers = false; +++ +++ // true if the currenly outstanding write operation is for null_buffers +++ bool m_send_null_buffers = false; +++ +++ // if this socket is connected to another endpoint, this object is +++ // shared between both sockets and contain information and state about +++ // the channel. +++ std::shared_ptr m_channel; +++ +++ std::uint64_t m_next_outgoing_seq = 0; +++ std::uint64_t m_next_incoming_seq = 0; +++ +++ // the sequence number of the last dropped packet. We should only cut +++ // the cwnd in half once per round-trip. If a whole window is lost, we +++ // need to only halve it once +++ std::uint64_t m_last_drop_seq = 0; +++ +++ // the current congestion window size (in bytes) +++ int m_cwnd = m_mss * 2; +++ +++ // the number of bytes that have been sent but not ACKed yet +++ int m_bytes_in_flight = 0; +++ +++ // reorder buffer for when packets are dropped +++ std::map, aux::mallocator>> m_reorder_buffer; +++ +++ // the sizes of packets given their sequence number +++ std::unordered_map, std::equal_to, aux::mallocator>> m_outstanding_packet_sizes; +++ +++ // packets to re-send (because they were dropped) +++ std::list> m_outgoing_packets; +++ }; +++ +++ struct SIMULATOR_DECL acceptor : socket +++ { +++ explicit acceptor(io_context& ios); +++ acceptor(acceptor&&); +++ ~acceptor() override; +++ +++ void cancel(boost::system::error_code& ec); +++ void cancel(); +++ +++ void listen(int qs = -1); +++ void listen(int qs, boost::system::error_code& ec); +++ +++ void async_accept(ip::tcp::socket& peer +++ , aux::function h); +++ void async_accept(ip::tcp::socket& peer +++ , ip::tcp::endpoint& peer_endpoint +++ , aux::function h); +++ void async_accept(aux::function h); +++ +++ void close(boost::system::error_code& ec); +++ void close(); +++ +++ // private interface +++ +++ // implements sink +++ virtual void incoming_packet(aux::packet p) override final; +++ virtual bool internal_is_listening() override final; +++ +++ private: +++ // check the incoming connection queue to see if any connection in +++ // there is ready to be accepted and delivered to the user +++ void check_accept_queue(); +++ void do_check_accept_queue(boost::system::error_code const& ec); +++ +++ aux::function m_accept_handler; +++ aux::function m_accept_handler2; +++ +++ // the number of half-open incoming connections this listen socket can +++ // hold. If this is -1, this socket is not yet listening and incoming +++ // connection attempts should be rejected. +++ int m_queue_size_limit; +++ +++ // these are incoming connection attempts. Both half-open and +++ // completely connected. When accepting a connection, this queue is +++ // checked first before waiting for a connection attempt. +++ using incoming_conns_t = std::vector, aux::mallocator>>; +++ incoming_conns_t m_incoming_conns; +++ +++ // for new-style accept, allocate socket in here just to fail early +++ boost::optional m_new_socket; +++ +++ // the socket to accept a connection into +++ tcp::socket* m_accept_into; +++ +++ // the endpoint to write the remote endpoint into when accepting +++ tcp::endpoint* m_remote_endpoint; +++ +++ // non copyable +++ acceptor(acceptor const&); +++ acceptor& operator=(acceptor const&); +++ }; +++ +++ using resolver = basic_resolver; +++ +++ friend bool operator==(tcp const& lhs, tcp const& rhs) +++ { return lhs.m_family == rhs.m_family; } +++ +++ friend bool operator!=(tcp const& lhs, tcp const& rhs) +++ { return lhs.m_family != rhs.m_family; } +++ +++ private: +++ // Construct with a specific family. +++ explicit tcp(int protocol_family) +++ : m_family(protocol_family) +++ {} +++ +++ int m_family; +++ }; +++ +++ extern template struct basic_resolver; +++ extern template struct basic_resolver; +++ +++ // boost::beast looks up a close function for a socket-like type via +++ // unqualified lookup + ADL under the name "beast_close_socket" (see +++ // boost/beast/core/stream_traits.hpp); this satisfies that customization +++ // point for tcp::socket - it's just a free function beast's ADL-based +++ // lookup will find because it lives in tcp::socket's own namespace, so +++ // beast::websocket::stream doesn't need this header to +++ // declare it inside boost::beast itself. +++ inline void beast_close_socket(tcp::socket& s) +++ { +++ boost::system::error_code ec; +++ s.close(ec); +++ } +++ +++ // same idea for the teardown()/async_teardown() customization points +++ // boost::beast::websocket::stream looks up (via ADL, from +++ // boost::beast::websocket) for any Socket type it's instantiated over +++ // that isn't a boost::asio socket -- see +++ // boost/beast/websocket/teardown.hpp. tcp::socket has no half-close/ +++ // non-blocking read-drain support to mirror the real graceful-shutdown +++ // sequence those implement for a real socket, so this just closes it; +++ // that's sufficient for driving deterministic test scenarios, which is +++ // the only thing this socket type is ever used for. +++ inline void teardown(boost::beast::role_type, tcp::socket& s, boost::system::error_code& ec) +++ { +++ s.close(ec); +++ } +++ +++ template +++ void async_teardown(boost::beast::role_type, tcp::socket& s, Handler&& handler) +++ { +++ boost::system::error_code ec; +++ s.close(ec); +++ boost::asio::post(s.get_executor(), std::bind(std::forward(handler), ec)); +++ } +++ +++ } // ip +++ +++ // mirrors (a subset of) boost::asio::async_connect(socket, begin, end, +++ // handler): tries each endpoint in the range in turn, closing and +++ // advancing to the next on failure, until one connects or the range is +++ // exhausted. Calls handler(error_code, Iterator) with the iterator that +++ // succeeded, or the end iterator carrying the last endpoint's error if +++ // all of them failed. Lives directly in sim::asio, alongside the +++ // boost::asio free function it mirrors, rather than in sim::asio::ip, so +++ // that unqualified calls resolve to whichever of the two applies via +++ // ADL (ip::tcp::socket's base, socket_base, is declared in this +++ // namespace, which is what makes that lookup work). +++ template +++ void async_connect(ip::tcp::socket& s, Iterator begin, Iterator end, Handler handler) +++ { +++ struct op : std::enable_shared_from_this +++ { +++ op(ip::tcp::socket& sock, Iterator b, Iterator e, Handler h) +++ : socket(sock), iter(b), end(e), handler(std::move(h)) +++ {} +++ +++ void attempt() +++ { +++ socket.async_connect(*iter, [self = this->shared_from_this()] +++ (boost::system::error_code const& ec) { self->on_connect(ec); }); +++ } +++ +++ void on_connect(boost::system::error_code const& ec) +++ { +++ if (ec && std::next(iter) != end) +++ { +++ boost::system::error_code ignore; +++ socket.close(ignore); +++ ++iter; +++ attempt(); +++ return; +++ } +++ +++ handler(ec, iter); +++ } +++ +++ ip::tcp::socket& socket; +++ Iterator iter; +++ Iterator end; +++ Handler handler; +++ }; +++ +++ if (begin == end) +++ { +++ handler(boost::asio::error::make_error_code(boost::asio::error::not_found), end); +++ return; +++ } +++ +++ std::make_shared(s, begin, end, std::move(handler))->attempt(); +++ } +++ +++ } // asio +++ +++ struct SIMULATOR_DECL simulation +++ { +++ // it calls fire() when a timer fires +++ friend struct high_resolution_timer; +++ +++ simulation(configuration& config); +++ ~simulation(); +++ +++ std::size_t run(); +++ +++ std::size_t poll(boost::system::error_code& ec); +++ std::size_t poll(); +++ +++ std::size_t poll_one(boost::system::error_code& ec); +++ std::size_t poll_one(); +++ +++ void stop(); +++ bool stopped() const; +++ void restart(); +++ // private interface +++ +++ void add_timer(asio::high_resolution_timer* t); +++ void remove_timer(asio::high_resolution_timer* t); +++ +++ boost::asio::io_context& get_internal_service() +++ { return m_service; } +++ +++ asio::io_context& get_io_context() +++ { return *m_internal_ios; } +++ +++ asio::ip::tcp::endpoint bind_socket(asio::ip::tcp::socket* socket +++ , asio::ip::tcp::endpoint ep +++ , boost::system::error_code& ec); +++ void unbind_socket(asio::ip::tcp::socket* socket +++ , asio::ip::tcp::endpoint const& ep); +++ void rebind_socket(asio::ip::tcp::socket* prev, asio::ip::tcp::socket* s, asio::ip::tcp::endpoint ep); +++ +++ asio::ip::udp::endpoint bind_udp_socket(asio::ip::udp::socket* socket +++ , asio::ip::udp::endpoint ep +++ , boost::system::error_code& ec); +++ void unbind_udp_socket(asio::ip::udp::socket* socket +++ , asio::ip::udp::endpoint const& ep); +++ void rebind_udp_socket(asio::ip::udp::socket* socket, asio::ip::udp::endpoint ep); +++ +++ std::shared_ptr internal_connect(asio::ip::tcp::socket* s +++ , asio::ip::tcp::endpoint const& target, boost::system::error_code& ec); +++ +++ route find_udp_socket( +++ asio::ip::udp::socket const& socket +++ , asio::ip::udp::endpoint const& ep); +++ +++ configuration& config() const { return m_config; } +++ +++ void add_io_service(asio::io_context* ios); +++ void remove_io_service(asio::io_context* ios); +++ std::vector get_all_io_services() const; +++ +++ aux::pcap* get_pcap() const { return m_pcap.get(); } +++ void log_pcap(char const* filename); +++ +++ private: +++ struct timer_compare +++ { +++ bool operator()(asio::high_resolution_timer const* lhs +++ , asio::high_resolution_timer const* rhs) const +++ { return lhs->expiry() < rhs->expiry(); } +++ }; +++ +++ configuration& m_config; +++ +++ std::unique_ptr m_pcap; +++ +++ // all non-expired timers +++ std::mutex m_timer_queue_mutex; +++ using timer_queue_t = std::vector>; +++ timer_queue_t m_timer_queue; +++ +++ // these are the io services that represent nodes on the network +++ std::unordered_set, std::equal_to, aux::mallocator> m_nodes; +++ +++ using listen_sockets_t = std::map; +++ using listen_socket_iter_t = listen_sockets_t::iterator; +++ listen_sockets_t m_listen_sockets; +++ +++ using udp_sockets_t = std::map; +++ using udp_socket_iter_t = udp_sockets_t::iterator; +++ udp_sockets_t m_udp_sockets; +++ +++ // used for internal timers. this is a pimpl sine our io_context is +++ // incomplete at this point +++ std::unique_ptr m_internal_ios; +++ +++ // the next port to use for an outgoing connection, where the port is not +++ // specified. We want this to be as unique as possible, to distinguish the +++ // TCP streams. +++ std::uint16_t m_next_bind_port = 2000; +++ +++ bool m_stopped = false; +++ +++ // underlying message queue +++ boost::asio::io_context m_service; +++ }; +++ +++ namespace asio { +++ +++ using boost::asio::async_write; +++ using boost::asio::async_read; +++ +++ // boost.asio compatible io_context class that simulates the network +++ // and time. +++ struct SIMULATOR_DECL io_context : boost::asio::execution_context +++ { +++ io_context(sim::simulation& sim); +++ io_context(sim::simulation& sim, ip::address const& ip); +++ io_context(sim::simulation& sim, std::vector const& ips); +++ io_context(std::size_t threads_hint = 0); +++ ~io_context(); +++ +++ // not copyable and non movable (it's not movable because we currently +++ // keep pointers to the io_context instances in the simulator object) +++ io_context(io_context const&) = delete; +++ io_context(io_context&&) = delete; +++ io_context& operator=(io_context const&) = delete; +++ io_context& operator=(io_context&&) = delete; +++ +++ std::size_t run(boost::system::error_code& ec); +++ std::size_t run(); +++ +++ std::size_t poll(boost::system::error_code& ec); +++ std::size_t poll(); +++ +++ std::size_t poll_one(boost::system::error_code& ec); +++ std::size_t poll_one(); +++ +++ template +++ std::size_t run_for(std::chrono::duration const&) +++ { +++ assert(false); +++ return 0; +++ } +++ +++ template +++ std::size_t poll_one_for(std::chrono::duration const&) +++ { +++ assert(false); +++ return 0; +++ } +++ +++ void stop(); +++ bool stopped() const; +++ void restart(); +++ +++ template +++ void dispatch_impl(Handler handler, Allocator const& a) +++ { get_internal_service().get_executor().dispatch(std::move(handler), a); } +++ +++ template +++ void post_impl(Handler handler, Allocator const& a) +++ { get_internal_service().get_executor().post(std::move(handler), a); } +++ +++ template +++ void defer_impl(Handler handler, Allocator const& a) +++ { get_internal_service().get_executor().defer(std::move(handler), a); } +++ +++ // internal interface +++ boost::asio::io_context& get_internal_service(); +++ +++ void add_timer(high_resolution_timer* t); +++ void remove_timer(high_resolution_timer* t); +++ +++ ip::tcp::endpoint bind_socket(ip::tcp::socket* socket, ip::tcp::endpoint ep +++ , boost::system::error_code& ec); +++ void unbind_socket(ip::tcp::socket* socket +++ , ip::tcp::endpoint const& ep); +++ void rebind_socket(asio::ip::tcp::socket* prev, asio::ip::tcp::socket* s, asio::ip::tcp::endpoint ep); +++ +++ ip::udp::endpoint bind_udp_socket(ip::udp::socket* socket, ip::udp::endpoint ep +++ , boost::system::error_code& ec); +++ void unbind_udp_socket(ip::udp::socket* socket +++ , ip::udp::endpoint const& ep); +++ void rebind_udp_socket(asio::ip::udp::socket* socket, asio::ip::udp::endpoint ep); +++ +++ std::shared_ptr internal_connect(ip::tcp::socket* s +++ , ip::tcp::endpoint const& target, boost::system::error_code& ec); +++ +++ route find_udp_socket(asio::ip::udp::socket const& socket +++ , ip::udp::endpoint const& ep); +++ +++ route const& get_outgoing_route(ip::address ip) const +++ { return m_outgoing_route.find(ip)->second; } +++ +++ route const& get_incoming_route(ip::address ip) const +++ { return m_incoming_route.find(ip)->second; } +++ +++ int get_path_mtu(const asio::ip::address& source, const asio::ip::address& dest) const; +++ std::vector const& get_ips() const { return m_ips; } +++ +++ sim::simulation& sim() { return m_sim; } +++ +++ using executor_type = io_executor; +++ executor_type get_executor() { return executor_type(*this); } +++ +++ private: +++ +++ sim::simulation& m_sim; +++ std::vector m_ips; +++ +++ // these are determined by the configuration. They may include NATs and +++ // DSL modems (queues) +++ std::map m_outgoing_route; +++ std::map m_incoming_route; +++ +++ bool m_stopped = false; +++ }; +++ +++ template +++ void io_executor::dispatch(Handler handler, Allocator const& a) const +++ { m_ctx->dispatch_impl(std::move(handler), a); } +++ +++ template +++ void io_executor::post(Handler handler, Allocator const& a) const +++ { m_ctx->post_impl(std::move(handler), a); } +++ +++ template +++ void io_executor::defer(Handler handler, Allocator const& a) const +++ { m_ctx->defer_impl(std::move(handler), a); } +++ +++ template +++ io_executor +++ socket_base::get_executor() +++ { return io_executor(m_io_service); } +++ +++ template +++ route socket_base::get_incoming_route() +++ { +++ route ret = m_io_service.get_incoming_route(m_bound_to.address()); +++ assert(m_forwarder); +++ ret.append(std::static_pointer_cast(m_forwarder)); +++ return ret; +++ } +++ +++ template +++ route socket_base::get_outgoing_route() +++ { +++ return route(m_io_service.get_outgoing_route(m_bound_to.address())); +++ } +++ +++ } // asio +++ +++ // user supplied configuration of the network to simulate +++ struct SIMULATOR_DECL configuration +++ { +++ virtual ~configuration() {} +++ +++ // build the network +++ virtual void build(simulation& sim) = 0; +++ +++ // return the hops on the network packets from src to dst need to traverse +++ virtual route channel_route(asio::ip::address src +++ , asio::ip::address dst) = 0; +++ +++ // return the hops an incoming packet to ep need to traverse before +++ // reaching the socket (for instance a NAT) +++ virtual route incoming_route(asio::ip::address ip) = 0; +++ +++ // return the hops an outgoing packet from ep need to traverse before +++ // reaching the network (for instance a DSL modem) +++ virtual route outgoing_route(asio::ip::address ip) = 0; +++ +++ // return the path MTU between the two IP addresses +++ // For TCP sockets, this will be called once when the connection is +++ // established. For UDP sockets it's called for every burst of packets +++ // that are sent +++ virtual int path_mtu(asio::ip::address ip1, asio::ip::address ip2) = 0; +++ +++ // called for every hostname lookup made by the client. ``reqyestor`` is +++ // the node performing the lookup, ``hostname`` is the name being looked +++ // up. Resolve the name into addresses and fill in ``result`` or set +++ // ``ec`` if the hostname is not found or some other error occurs. The +++ // return value is the latency of the lookup. The client's callback won't +++ // be called until after waiting this long. +++ virtual chrono::high_resolution_clock::duration hostname_lookup( +++ asio::ip::address const& requestor +++ , std::string hostname +++ , std::vector& result +++ , boost::system::error_code& ec) = 0; +++ +++ virtual void clear() = 0; +++ }; +++ +++ struct SIMULATOR_DECL default_config : configuration +++ { +++ default_config() : m_sim(nullptr) {} +++ +++ void build(simulation& sim) override; +++ route channel_route(asio::ip::address src, asio::ip::address dst) override; +++ route incoming_route(asio::ip::address ip) override; +++ route outgoing_route(asio::ip::address ip) override; +++ int path_mtu(asio::ip::address ip1, asio::ip::address ip2) override; +++ chrono::high_resolution_clock::duration hostname_lookup( +++ asio::ip::address const& requestor +++ , std::string hostname +++ , std::vector& result +++ , boost::system::error_code& ec) override; +++ +++ void clear() override; +++ protected: +++ std::shared_ptr m_network; +++ std::map> m_incoming; +++ std::map> m_outgoing; +++ simulation* m_sim; +++ }; +++ +++ namespace aux +++ { +++ /* the channel can be in the following states: +++ 1. handshake-1 - the initiating socket has sent SYN +++ 2. handshake-2 - the accepting connection has sent SYN+ACK +++ 3. handshake-3 - the initiating connection has received the SYN+ACK and +++ considers the connection open, but the 3rd handshake +++ message is still in flight. +++ 4. connected - the accepting side has received the 3rd handshake +++ packet and considers it open +++ +++ Whenever a connection attempt is made to a listening socket, as long as +++ there is still space in the incoming socket queue, the accepting side +++ will always respond immediately and complete the handshake, then wait +++ until the user calls async_accept (which in this case would complete +++ immediately). +++ */ +++ struct SIMULATOR_DECL channel +++ { +++ channel() {} +++ // index 0 is the incoming route to the socket that initiated the connection. +++ // index 1 may be empty while the connection is half-open +++ route hops[2]; +++ +++ // the actual endpoint of each end of the channel +++ asio::ip::tcp::endpoint ep[2]; +++ +++ // observable endpoint of each side of the channel. This is not how you +++ // see yourself, just the other end +++ asio::ip::tcp::endpoint visible_ep[2]; +++ +++ // the number of bytes sent from respective direction +++ // this is used to simulate the TCP sequence number, so it deliberately +++ // is meant to wrap at 32 bits +++ std::uint32_t bytes_sent[2]; +++ +++ int remote_idx(asio::ip::tcp::endpoint const& self) const; +++ int self_idx(asio::ip::tcp::endpoint const& self) const; +++ }; +++ +++ } // aux +++ +++ void SIMULATOR_DECL dump_network_graph(simulation const& s, const std::string& filename); +++} +++ +++#ifdef _MSC_VER +++#pragma warning(pop) +++#endif +++ +++#endif // SIMULATOR_HPP_INCLUDED +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/sink.hpp ++@@ -0,0 +1,47 @@ +++/* +++ +++Copyright (c) 2015, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#ifndef SINK_HPP_INCLUDED +++#define SINK_HPP_INCLUDED +++ +++#include "simulator/config.hpp" +++#include +++ +++namespace sim { +++ +++namespace aux { +++ struct packet; +++} +++ +++ // this is an interface for somthing that can accept incoming packets, +++ // such as queues, sockets, NATs and TCP congestion windows +++ struct SIMULATOR_DECL sink +++ { +++ virtual void incoming_packet(aux::packet p) = 0; +++ +++ // used for visualization +++ virtual std::string label() const = 0; +++ +++ virtual std::string attributes() const { return "shape=box"; } +++ virtual ~sink() = default; +++ }; +++ +++} // sim +++ +++#endif +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/sink_forwarder.hpp ++@@ -0,0 +1,43 @@ +++/* +++ +++Copyright (c) 2015, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#ifndef SINK_FORWARDER_HPP_INCLUDED +++#define SINK_FORWARDER_HPP_INCLUDED +++ +++#include "simulator/config.hpp" +++#include "simulator/sink.hpp" +++ +++namespace sim { namespace aux { +++ +++ struct packet; +++ +++ struct SIMULATOR_DECL sink_forwarder final : sink +++ { +++ sink_forwarder(sink* dst); +++ void incoming_packet(packet p) override; +++ std::string label() const override; +++ void reset(sink* s = nullptr); +++ +++ private: +++ sink* m_dst; +++ }; +++ +++}} // sim +++ +++#endif +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/socks_server.hpp ++@@ -0,0 +1,202 @@ +++/* +++ +++Copyright (c) 2015, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#ifndef SOCKS_SERVER_HPP_INCLUDED +++#define SOCKS_SERVER_HPP_INCLUDED +++ +++#include "simulator/simulator.hpp" +++ +++#ifdef __GNUC__ +++#pragma GCC diagnostic push +++#if __GNUC__ >= 9 +++#pragma GCC diagnostic ignored "-Wparentheses" +++#endif +++#endif +++ +++#include +++ +++#ifdef __GNUC__ +++#pragma GCC diagnostic pop +++#endif +++ +++#ifdef _MSC_VER +++#pragma warning(push) +++// warning C4251: X: class Y needs to have dll-interface to be used by clients of struct +++#pragma warning( disable : 4251) +++#endif +++ +++namespace sim +++{ +++ +++enum socks_flag +++{ +++ // when this flag is set, the proxy will close the client connection +++ // immediately after sending the response to a UDP ASSOCIATE command +++ disconnect_udp_associate = 1, +++ +++ // when this flag is set, the reponse to UDP ASSOCIATE will contain an empty +++ // hostname, rather than the relay IP address +++ udp_associate_respond_empty_hostname = 2 +++}; +++ +++struct SIMULATOR_DECL socks_connection : std::enable_shared_from_this +++{ +++ socks_connection(asio::io_context& ios, int version, std::array& cmd_counts +++ , std::uint32_t flags, int& bind_port); +++ +++ asio::ip::tcp::socket& socket() { return m_client_connection; } +++ +++ void start(); +++ +++private: +++ +++ void on_handshake1(boost::system::error_code const& ec, size_t bytes_transferred); +++ void on_handshake2(boost::system::error_code const& ec, size_t bytes_transferred); +++ void on_handshake3(boost::system::error_code const& ec, size_t bytes_transferred); +++ void on_request1(boost::system::error_code const& ec, size_t bytes_transferred); +++ void on_request2(boost::system::error_code const& ec, size_t bytes_transferred); +++ +++ void on_write(boost::system::error_code const& ec, size_t bytes_transferred +++ , bool close); +++ void close_connection(); +++ +++ int format_response(asio::ip::address const& addr, int port, int response); +++ int format_hostname_response(char const* hostname, int port, int response); +++ +++ void on_connected(boost::system::error_code const& ec); +++ void on_request_domain_name(boost::system::error_code const& ec, size_t bytes_transferred); +++ void on_request_domain_lookup(boost::system::error_code const& ec +++ , const asio::ip::tcp::resolver::results_type ips); +++ +++ void open_forward_connection(asio::ip::tcp::endpoint const& target); +++ void bind_connection(asio::ip::tcp::endpoint const& target); +++ void start_accept(boost::system::error_code const& ec); +++ +++ void udp_associate(asio::ip::tcp::endpoint const& target); +++ void on_read_udp(boost::system::error_code const& ec, std::size_t bytes_transferred); +++ void wait_for_eof(boost::system::error_code const& ec, std::size_t bytes_transferred); +++ +++ void on_server_receive(boost::system::error_code const& ec +++ , std::size_t bytes_transferred); +++ void on_server_forward(boost::system::error_code const& ec +++ , size_t bytes_transferred); +++ +++ void on_client_receive(boost::system::error_code const& ec +++ , std::size_t bytes_transferred); +++ void on_client_forward(boost::system::error_code const& ec +++ , size_t bytes_transferred); +++ +++ char const* command() const; +++ +++ int& m_bind_port; +++ +++ asio::io_context& m_ios; +++ +++ asio::ip::udp::resolver m_udp_resolver; +++ +++ asio::ip::tcp::resolver m_resolver; +++ +++ boost::bimap m_name_mapping; +++ +++ // this is the SOCKS client connection, i.e. the client connecting to us and +++ // being forwarded +++ asio::ip::tcp::socket m_client_connection; +++ +++ // this is the connection to the server the socks client is being forwarded +++ // to +++ asio::ip::tcp::socket m_server_connection; +++ asio::ip::tcp::acceptor m_bind_socket; +++ +++ asio::ip::udp::socket m_udp_associate; +++ asio::ip::udp::endpoint m_udp_associate_ep; +++ asio::ip::udp::endpoint m_udp_from; +++ +++ std::array m_udp_buffer; +++ +++ // receive buffer for data going out, i.e. client -> proxy (us) -> server +++ char m_out_buffer[65536]; +++ // buffer size +++ int m_num_out_bytes; +++ +++ // receive buffer for data coming in, i.e. server -> proxy (us) -> client +++ char m_in_buffer[65536]; +++ // buffer size +++ int m_num_in_bytes; +++ +++ // set to true when shutting down +++ bool m_close = false; +++ +++ // the SOCKS protocol version (4 or 5) +++ const int m_version; +++ +++ int m_command; +++ +++ std::array& m_cmd_counts; +++ +++ std::uint32_t const m_flags; +++}; +++ +++// This is a very simple socks4 and 5 server that only supports a single +++// concurrent connection +++struct SIMULATOR_DECL socks_server +++{ +++ socks_server(asio::io_context& ios, unsigned short listen_port +++ , int version = 5, std::uint32_t flags = 0); +++ +++ void stop(); +++ +++ void bind_start_port(int const port) { m_bind_port = port; } +++ +++ // return the number of CONNECT, BIND and UDP_ASSOCIATE commands the proxy +++ // has received +++ std::array cmd_counts() const +++ { return m_cmd_counts; } +++ +++private: +++ +++ void on_accept(boost::system::error_code const& ec); +++ +++ asio::io_context& m_ios; +++ +++ asio::ip::tcp::acceptor m_listen_socket; +++ +++ std::shared_ptr m_conn; +++ +++ asio::ip::tcp::endpoint m_ep; +++ +++ int m_bind_port = 2048; +++ +++ // set to true when shutting down +++ bool m_close = false; +++ +++ // the SOCKS protocol version (4 or 5) +++ const int m_version; +++ +++ std::array m_cmd_counts; +++ +++ std::uint32_t const m_flags; +++}; +++ +++} +++ +++#ifdef _MSC_VER +++#pragma warning(pop) +++#endif +++ +++#endif +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/ssl.hpp ++@@ -0,0 +1,44 @@ +++/* +++ +++Copyright (c) 2026, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#ifndef SIMULATOR_SSL_HPP_INCLUDED +++#define SIMULATOR_SSL_HPP_INCLUDED +++ +++#include "simulator/simulator.hpp" +++ +++#include "simulator/push_warnings.hpp" +++#include +++#include "simulator/pop_warnings.hpp" +++ +++namespace sim { +++namespace asio { +++ +++namespace ssl +++{ +++ using boost::asio::ssl::context; +++ using boost::asio::ssl::stream_base; +++ using boost::asio::ssl::verify_context; +++ +++ template +++ using stream = boost::asio::ssl::stream; +++} // ssl +++ +++} // asio +++} // sim +++ +++#endif ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/utils.hpp ++@@ -0,0 +1,47 @@ +++/* +++ +++Copyright (c) 2016, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#ifndef UTILS_HPP_INCLUDED +++#define UTILS_HPP_INCLUDED +++ +++#include "simulator/simulator.hpp" +++ +++namespace sim +++{ +++ +++// shortcut for creating a timer with a timeout and action +++struct timer +++{ +++ timer(simulation& sim, chrono::high_resolution_clock::duration timeout +++ , aux::function&& f) +++ : m_ios(sim, asio::ip::address_v4()) +++ , m_timer(m_ios) +++ { +++ m_timer.expires_after(timeout); +++ m_timer.async_wait(std::move(f)); +++ } +++ +++private: +++ sim::asio::io_context m_ios; +++ sim::asio::high_resolution_timer m_timer; +++}; +++ +++} // sim +++ +++#endif +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/acceptor.cpp ++@@ -0,0 +1,379 @@ +++/* +++ +++Copyright (c) 2015, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#include "simulator/simulator.hpp" +++#include "simulator/packet.hpp" +++ +++#include +++#include +++ +++#include +++ +++using boost::beast::bind_handler; +++ +++typedef sim::chrono::high_resolution_clock::time_point time_point; +++typedef sim::chrono::high_resolution_clock::duration duration; +++ +++namespace sim { +++namespace asio { +++namespace ip { +++ +++ tcp::acceptor::acceptor(io_context& ios) +++ : socket(ios) +++ , m_queue_size_limit(-1) +++ {} +++ +++ tcp::acceptor::acceptor(acceptor&&) = default; +++ +++ tcp::acceptor::~acceptor() +++ { +++ boost::system::error_code ec; +++ close(ec); +++ } +++ +++ void tcp::acceptor::listen(int qs) +++ { +++ boost::system::error_code ec; +++ listen(qs, ec); +++ if (ec) throw boost::system::system_error(ec); +++ } +++ +++ void tcp::acceptor::listen(int qs, boost::system::error_code& ec) +++ { +++ if (qs == -1) qs = 20; +++ +++ if (!m_open) +++ { +++ ec = error::bad_descriptor; +++ return; +++ } +++ if (m_bound_to == ip::tcp::endpoint()) +++ { +++ ec = error::invalid_argument; +++ return; +++ } +++ +++ m_queue_size_limit = qs; +++ ec.clear(); +++ } +++ +++ void tcp::acceptor::close(boost::system::error_code& ec) +++ { +++ m_queue_size_limit = -1; +++ cancel(ec); +++ socket::close(ec); +++ } +++ +++ void tcp::acceptor::close() +++ { +++ if (m_accept_handler) +++ { +++ post(m_io_service, bind_handler(std::exchange(m_accept_handler, nullptr) +++ , boost::system::error_code(error::operation_aborted))); +++ } +++ if (m_accept_handler2) +++ { +++ post(m_io_service, [&, h = std::exchange(m_accept_handler2, nullptr)] () mutable { +++ h(boost::system::error_code(error::operation_aborted) +++ , ip::tcp::socket(m_io_service)); +++ }); +++ } +++ } +++ +++ void tcp::acceptor::cancel(boost::system::error_code& ec) +++ { +++ ec.clear(); +++ if (m_accept_handler) +++ { +++ try +++ { +++ post(m_io_service, bind_handler(std::exchange(m_accept_handler, nullptr) +++ , boost::system::error_code(error::operation_aborted))); +++ } +++ catch (std::bad_alloc const&) +++ { +++ ec = error::no_memory; +++ } +++ catch (std::exception const&) +++ { +++ ec = error::no_memory; +++ } +++ } +++ if (m_accept_handler2) +++ { +++ try +++ { +++ post(m_io_service, [&, h = std::exchange(m_accept_handler2, nullptr)] () mutable { +++ h(boost::system::error_code(error::operation_aborted) +++ , ip::tcp::socket(m_io_service)); +++ }); +++ } +++ catch (std::bad_alloc const&) +++ { +++ ec = error::no_memory; +++ } +++ catch (std::exception const&) +++ { +++ ec = error::no_memory; +++ } +++ } +++ } +++ +++ void tcp::acceptor::cancel() +++ { +++ boost::system::error_code ec; +++ cancel(ec); +++ if (ec) throw boost::system::system_error(ec); +++ } +++ +++ void tcp::acceptor::async_accept(ip::tcp::socket& peer +++ , aux::function h) +++ { +++ // TODO: assert that the io_context we use is the same as the one peer use +++ if (peer.is_open()) +++ { +++ boost::system::error_code ec; +++ peer.close(ec); +++ } +++ +++ if (m_accept_handler) +++ { +++ m_accept_into = nullptr; +++ m_remote_endpoint = nullptr; +++ post(m_io_service, bind_handler(std::exchange(m_accept_handler, nullptr) +++ , boost::system::error_code(error::operation_aborted))); +++ } +++ if (m_accept_handler2) +++ { +++ m_accept_into = nullptr; +++ m_remote_endpoint = nullptr; +++ post(m_io_service, [&, h = std::exchange(m_accept_handler2, nullptr)] () mutable { +++ h(boost::system::error_code(error::operation_aborted) +++ , ip::tcp::socket(m_io_service)); +++ }); +++ } +++ m_accept_handler = std::move(h); +++ m_accept_into = &peer; +++ m_remote_endpoint = nullptr; +++ +++ check_accept_queue(); +++ } +++ +++ void tcp::acceptor::async_accept(ip::tcp::socket& peer +++ , ip::tcp::endpoint& peer_endpoint +++ , aux::function h) +++ { +++ if (peer.is_open()) +++ { +++ boost::system::error_code ec; +++ peer.close(ec); +++ } +++ +++ if (m_accept_handler) +++ { +++ post(m_io_service, bind_handler(std::exchange(m_accept_handler, nullptr) +++ , boost::system::error_code(error::operation_aborted))); +++ } +++ if (m_accept_handler2) +++ { +++ post(m_io_service, [&, h = std::exchange(m_accept_handler2, nullptr)] () mutable { +++ h(boost::system::error_code(error::operation_aborted) +++ , ip::tcp::socket(m_io_service)); +++ }); +++ } +++ m_accept_handler = std::move(h); +++ m_accept_into = &peer; +++ m_remote_endpoint = &peer_endpoint; +++ +++ check_accept_queue(); +++ } +++ +++ void tcp::acceptor::async_accept(aux::function h) +++ { +++ m_remote_endpoint = nullptr; +++ if (m_accept_handler) +++ { +++ m_accept_into = nullptr; +++ post(m_io_service, bind_handler(std::exchange(m_accept_handler, nullptr) +++ , boost::system::error_code(error::operation_aborted))); +++ } +++ if (m_accept_handler2) +++ { +++ m_accept_into = nullptr; +++ post(m_io_service, [&, h = std::exchange(m_accept_handler2, nullptr)] () mutable { +++ h(boost::system::error_code(error::operation_aborted) +++ , ip::tcp::socket(m_io_service)); +++ }); +++ } +++ m_new_socket.emplace(m_io_service); +++ m_accept_handler2 = std::move(h); +++ m_accept_into = &*m_new_socket; +++ +++ check_accept_queue(); +++ } +++ +++ void tcp::acceptor::do_check_accept_queue(boost::system::error_code const& ec) +++ { +++ if (ec) return; +++ check_accept_queue(); +++ } +++ +++ void tcp::acceptor::incoming_packet(aux::packet p) +++ { +++ switch (p.type) +++ { +++ case aux::packet::type_t::syn: +++ m_incoming_conns.push_back(p.channel); +++ check_accept_queue(); +++ return; +++ case aux::packet::type_t::error: +++ assert(false); // something is not wired up correctly +++ if (m_accept_handler) +++ { +++ m_accept_into = nullptr; +++ m_remote_endpoint = nullptr; +++ post(m_io_service, bind_handler(std::exchange(m_accept_handler, nullptr) +++ , boost::system::error_code(error::operation_aborted))); +++ } +++ if (m_accept_handler2) +++ { +++ m_accept_into = nullptr; +++ m_remote_endpoint = nullptr; +++ post(m_io_service, [&, h = std::exchange(m_accept_handler2, nullptr)] () mutable { +++ h(boost::system::error_code(error::operation_aborted) +++ , ip::tcp::socket(m_io_service)); +++ }); +++ } +++ return; +++ default: +++ // if this happens, it implies that an incoming connection sent +++ // payload before receiving a syn_ack. Alternatively that the +++ // acceptor sent the syn_ack but still left the last-hop in the +++ // incoming route to point to this socket, instead of the +++ // accepted-into socket +++ assert(false); +++ return; +++ } +++ } +++ +++ void tcp::acceptor::check_accept_queue() +++ { +++ if (!is_open()) +++ { +++ // if the acceptor socket is closed. Any potential socket in the queue +++ // should be closed too. +++ for (auto const& incoming : m_incoming_conns) +++ { +++ aux::packet p; +++ p.from = asio::ip::udp::endpoint( +++ m_bound_to.address(), m_bound_to.port()); +++ p.type = aux::packet::type_t::error; +++ p.ec = boost::system::error_code(error::connection_reset); +++ p.overhead = 28; +++ p.hops = incoming->hops[0]; +++ +++ forward_packet(std::move(p)); +++ } +++ m_incoming_conns.clear(); +++ +++ if (m_accept_handler) +++ { +++ m_accept_into = nullptr; +++ m_remote_endpoint = nullptr; +++ post(m_io_service, bind_handler(std::exchange(m_accept_handler, nullptr) +++ , boost::system::error_code(error::operation_aborted))); +++ } +++ if (m_accept_handler2) +++ { +++ m_accept_into = nullptr; +++ m_remote_endpoint = nullptr; +++ post(m_io_service, [&, h = std::exchange(m_accept_handler2, nullptr)] () mutable { +++ h(boost::system::error_code(error::operation_aborted) +++ , ip::tcp::socket(m_io_service)); +++ }); +++ } +++ } +++ +++ // if the user is not waiting for an incoming connection, there's no point +++ // in checking the queue +++ if (!m_accept_handler && !m_accept_handler2) return; +++ +++ if (m_incoming_conns.empty()) return; +++ +++ std::shared_ptr c = std::move(m_incoming_conns.front()); +++ m_incoming_conns.erase(m_incoming_conns.begin()); +++ +++ // this was initiated at least one 3-way handshake ago. +++ // we can pick it up and consider it connected +++ if (m_remote_endpoint) *m_remote_endpoint = c->ep[0]; +++ m_remote_endpoint = nullptr; +++ +++ boost::system::error_code ec; +++ // if the acceptor socket is closed. Any potential socket in the queue +++ m_accept_into->internal_connect(m_bound_to, c, ec); +++ +++ // notify the other end +++ aux::packet p; +++ p.from = asio::ip::udp::endpoint(m_bound_to.address(), m_bound_to.port()); +++ if (ec) +++ { +++ c->hops[1] = route(); +++ p.type = aux::packet::type_t::error; +++ p.ec = ec; +++ } +++ else +++ { +++ // TODO: extend pcap logging to include SYN+ACK packets +++ p.type = aux::packet::type_t::syn_ack; +++ } +++ p.channel = c; +++ p.overhead = 28; +++ p.hops = p.channel->hops[0]; +++ +++ forward_packet(std::move(p)); +++ +++ try +++ { +++ if (m_accept_handler) +++ { +++ post(m_io_service, bind_handler(std::exchange(m_accept_handler, nullptr), ec)); +++ } +++ else if (m_accept_handler2) +++ { +++ post(m_io_service, bind_handler(std::exchange(m_accept_handler2, nullptr) +++ , ec, std::move(*m_accept_into))); +++ } +++ } +++ catch (...) +++ { +++ m_new_socket.reset(); +++ m_accept_into = nullptr; +++ throw; +++ } +++ m_new_socket.reset(); +++ m_accept_into = nullptr; +++ } +++ +++ bool tcp::acceptor::internal_is_listening() +++ { +++ return m_queue_size_limit > 0; +++ } +++} +++} +++} +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/default_config.cpp ++@@ -0,0 +1,106 @@ +++/* +++ +++Copyright (c) 2015, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#include "simulator/simulator.hpp" +++#include "simulator/queue.hpp" +++#include +++ +++typedef sim::chrono::high_resolution_clock::time_point time_point; +++typedef sim::chrono::high_resolution_clock::duration duration; +++using sim::asio::ip::address_v4; +++using sim::asio::ip::address_v6; +++using sim::asio::ip::address; +++using sim::chrono::milliseconds; +++using sim::chrono::duration_cast; +++ +++namespace sim { +++ +++ void default_config::build(simulation& sim) +++ { +++ // 0 bandwidth and 0 queue means infinite. The network itself only adds +++ // 50 ms latency +++ m_network = std::make_shared(std::ref(sim.get_io_context()) +++ , 0, duration_cast(milliseconds(30)), 0, "network"); +++ m_sim = ∼ +++ } +++ +++ void default_config::clear() +++ { +++ m_network.reset(); +++ m_outgoing.clear(); +++ m_incoming.clear(); +++ } +++ +++ route default_config::channel_route( +++ asio::ip::address /* src */ +++ , asio::ip::address /* dst */) +++ { +++ return route().append(m_network); +++ } +++ +++ route default_config::incoming_route(asio::ip::address ip) +++ { +++ // incoming download rate is 800kB/s with a 200 kB queue +++ // and 1 ms forwarding delay +++ auto it = m_incoming.find(ip); +++ if (it != m_incoming.end()) return route().append(it->second); +++ it = m_incoming.insert(it, std::make_pair(ip, std::make_shared( +++ std::ref(m_sim->get_io_context()), 800 * 1000 +++ , duration_cast(milliseconds(1)), 200 * 1000, "DSL modem in"))); +++ return route().append(it->second); +++ } +++ +++ int default_config::path_mtu( +++ asio::ip::address /* ip1 */ +++ , asio::ip::address /* ip2 */) +++ { +++ return 1475; +++ } +++ +++ // return the hops an outgoing packet from ep need to traverse before +++ // reaching the network (for instance a DSL modem) +++ route default_config::outgoing_route(asio::ip::address ip) +++ { +++ // outgoing upload rate is 200kB/s with a 200 kB queue +++ // and 1 ms forwarding delay +++ auto it = m_outgoing.find(ip); +++ if (it != m_outgoing.end()) return route().append(it->second); +++ it = m_outgoing.insert(it, std::make_pair(ip, std::make_shared( +++ std::ref(m_sim->get_io_context()), 200 * 1000 +++ , duration_cast(milliseconds(1)), 200 * 1000, "DSL modem out"))); +++ return route().append(it->second); +++ } +++ +++ duration default_config::hostname_lookup( +++ asio::ip::address const& /* requestor */ +++ , std::string hostname +++ , std::vector& result +++ , boost::system::error_code& ec) +++ { +++ if (hostname == "localhost") +++ { +++ result = { asio::ip::make_address_v6("::1") +++ , asio::ip::make_address_v4("127.0.0.1") }; +++ return duration_cast(chrono::microseconds(1)); +++ } +++ +++ ec = boost::system::error_code(asio::error::host_not_found); +++ return duration_cast(chrono::milliseconds(100)); +++ } +++} +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/high_resolution_clock.cpp ++@@ -0,0 +1,51 @@ +++/* +++ +++Copyright (c) 2015, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#include "simulator/simulator.hpp" +++ +++#include +++ +++#include "simulator/push_warnings.hpp" +++#include +++#include "simulator/pop_warnings.hpp" +++ +++namespace sim { namespace chrono { +++ namespace { +++ +++ // this is the global simulation timer +++ high_resolution_clock::time_point g_simulation_time; +++ } +++ +++ high_resolution_clock::time_point high_resolution_clock::now() +++ { +++ return g_simulation_time; +++ } +++ +++ void high_resolution_clock::fast_forward(high_resolution_clock::duration d) +++ { +++ g_simulation_time += d; +++ } +++ +++ void reset_clock() +++ { +++ g_simulation_time = high_resolution_clock::time_point{}; +++ } +++ +++} // chrono +++} // sim +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/high_resolution_timer.cpp ++@@ -0,0 +1,141 @@ +++/* +++ +++Copyright (c) 2015, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#include "simulator/simulator.hpp" +++#include "simulator/handler_allocator.hpp" +++ +++#include +++#include +++ +++namespace sim +++{ +++ +++ namespace asio { +++ +++ high_resolution_timer::high_resolution_timer(io_context& ioc) +++ : m_expiration_time(time_type()) +++ , m_io_service(&ioc) +++ , m_expired(true) +++ { +++ } +++ +++ high_resolution_timer::high_resolution_timer(io_context& ioc, +++ const time_type& expiry_time) +++ : m_expiration_time(time_type()) +++ , m_io_service(&ioc) +++ , m_expired(true) +++ { +++ expires_at(expiry_time); +++ } +++ +++ high_resolution_timer::high_resolution_timer(io_context& ioc, +++ const duration_type& expiry_time) +++ : m_expiration_time(time_type()) +++ , m_io_service(&ioc) +++ , m_expired(true) +++ { +++ expires_after(expiry_time); +++ } +++ +++ high_resolution_timer::~high_resolution_timer() +++ { +++ cancel(); +++ } +++ +++ std::size_t high_resolution_timer::cancel() +++ { +++ if (m_expired) return 0; +++ m_expired = true; +++ m_io_service->remove_timer(this); +++ if (!m_handler) return 0; +++ fire(boost::asio::error::operation_aborted); +++ return 1; +++ } +++ +++ std::size_t high_resolution_timer::cancel_one() +++ { +++ // TODO: support multiple handlers +++ return cancel(); +++ } +++ +++ high_resolution_timer::time_type high_resolution_timer::expiry() const +++ { return m_expiration_time; } +++ +++ std::size_t high_resolution_timer::expires_at(high_resolution_timer::time_type const& expiry_time) +++ { +++ std::size_t ret = cancel(); +++ m_expiration_time = expiry_time; +++ m_expired = false; +++ m_io_service->add_timer(this); +++ return ret; +++ } +++ +++ std::size_t high_resolution_timer::expires_after(const duration_type& expiry_time) +++ { +++ std::size_t ret = cancel(); +++ m_expiration_time = chrono::high_resolution_clock::now() + expiry_time; +++ m_expired = false; +++ m_io_service->add_timer(this); +++ return ret; +++ } +++ +++ void high_resolution_timer::wait() +++ { +++ assert(false && "can't use synchcronous calls in simulator"); +++ boost::system::error_code ec; +++ wait(ec); +++ } +++ +++ void high_resolution_timer::wait(boost::system::error_code&) +++ { +++ assert(false && "can't use synchcronous calls in simulator"); +++ time_type now = chrono::high_resolution_clock::now(); +++ if (now >= m_expiration_time) return; +++ chrono::high_resolution_clock::fast_forward(m_expiration_time - now); +++ } +++ +++ void high_resolution_timer::async_wait(aux::function handler) +++ { +++ // TODO: support multiple handlers +++ assert(!m_handler); +++ m_handler = std::move(handler); +++ if (m_expired) +++ { +++ fire(boost::system::error_code()); +++ return; +++ } +++ } +++ +++ void high_resolution_timer::fire(boost::system::error_code ec) +++ { +++ m_expired = true; +++ if (!m_handler) return; +++ auto h = std::move(m_handler); +++ m_handler = nullptr; +++ post(*m_io_service, make_malloc(std::bind(std::move(h), ec))); +++ } +++ +++ high_resolution_timer::executor_type high_resolution_timer::get_executor() +++ { +++ return (*m_io_service).get_executor(); +++ } +++ +++ } // asio +++ +++} // sim +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/http_proxy.cpp ++@@ -0,0 +1,362 @@ +++/* +++ +++Copyright (c) 2015, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#include "simulator/simulator.hpp" +++#include "simulator/http_proxy.hpp" +++#include "simulator/http_server.hpp" // for helper functions +++ +++#include +++#include // for printf +++ +++using namespace sim::asio; +++using namespace sim::asio::ip; +++using namespace std::placeholders; +++ +++using boost::system::error_code; +++ +++namespace sim +++{ +++ using namespace aux; +++ +++ http_proxy::http_proxy(io_context& ios, unsigned short const listen_port) +++ : m_resolver(ios) +++ , m_listen_socket(ios) +++ , m_client_connection(ios) +++ , m_server_connection(ios) +++ , m_writing_to_server(false) +++ , m_num_client_in_bytes(0) +++ , m_num_server_out_bytes(0) +++ , m_num_in_bytes(0) +++ , m_close(false) +++ { +++ address local_ip = ios.get_ips().front(); +++ if (local_ip.is_v4()) +++ { +++ m_listen_socket.open(tcp::v4()); +++ m_listen_socket.bind(tcp::endpoint(address_v4::any(), listen_port)); +++ } +++ else +++ { +++ m_listen_socket.open(tcp::v6()); +++ m_listen_socket.bind(tcp::endpoint(address_v6::any(), listen_port)); +++ } +++ m_listen_socket.listen(); +++ +++ m_listen_socket.async_accept(m_client_connection, m_ep +++ , std::bind(&http_proxy::on_accept, this, _1)); +++ } +++ +++ void http_proxy::on_accept(error_code const& ec) +++ { +++ if (ec == asio::error::operation_aborted) +++ return; +++ +++ if (ec) +++ { +++ std::printf("http_proxy::on_accept: (%d) %s\n" +++ , ec.value(), ec.message().c_str()); +++ close_connection(); +++ return; +++ } +++ +++ std::printf("http_proxy accepted connection from: %s : %d\n", +++ m_ep.address().to_string().c_str(), m_ep.port()); +++ +++ // read http request +++ m_client_connection.async_read_some(asio::buffer( +++ &m_client_in_buffer[0], sizeof(m_client_in_buffer)) +++ , std::bind(&http_proxy::on_read_request, this, _1, _2)); +++ } +++ +++ void http_proxy::on_read_request(error_code const& ec, size_t bytes_transferred) try +++ { +++ if (ec) +++ { +++ std::printf("http_proxy::on_read_request: (%d) %s\n" +++ , ec.value(), ec.message().c_str()); +++ close_connection(); +++ return; +++ } +++ +++ m_num_client_in_bytes += int(bytes_transferred); +++ +++ // scan for end of HTTP request +++ int req_len = find_request_len(m_client_in_buffer, m_num_client_in_bytes); +++ while (req_len >= 0) +++ { +++ // parse request from [0, eor), connect to target server and forward +++ // the request. +++ http_request const req = parse_request(m_client_in_buffer, req_len); +++ forward_request(req); +++ +++ // pop this request from the receive buffer +++ memmove(m_client_in_buffer, m_client_in_buffer + req_len +++ , m_num_client_in_bytes - req_len); +++ m_num_client_in_bytes -= req_len; +++ +++ // is there another request in the buffer? +++ req_len = find_request_len(m_client_in_buffer, m_num_client_in_bytes); +++ } +++ +++ // read more from the client +++ m_client_connection.async_read_some(asio::buffer( +++ &m_client_in_buffer[m_num_client_in_bytes] +++ , sizeof(m_client_in_buffer) - m_num_client_in_bytes) +++ , std::bind(&http_proxy::on_read_request, this, _1, _2)); +++ } +++ catch (std::runtime_error& e) +++ { +++ std::printf("http_proxy::on_read_request() failed: %s\n" +++ , e.what()); +++ close_connection(); +++ } +++ +++ void http_proxy::forward_request(http_request const& req) +++ { +++ std::string out_request; +++ out_request = req.method; +++ out_request += ' '; +++ if (req.req.compare(0, 7, "http://") != 0) +++ { +++ std::printf("http_proxy::forward_request: expected full URL in request, got: %s\n" +++ , req.req.c_str()); +++ throw std::runtime_error("invalid request"); +++ } +++ +++ std::string::size_type const path_start = req.req.find_first_of('/', 7); +++ if (path_start == std::string::npos) out_request += '/'; +++ else out_request.append(req.req, path_start, std::string::npos); +++ out_request += " HTTP/1.1\r\n"; +++ +++ std::string::size_type const host_end = req.req.substr(0, path_start).find_last_of(':'); +++ +++ std::string host = req.req.substr(7, (host_end != std::string::npos && host_end > 7) +++ ? host_end - 7 : path_start - 7); +++ +++ // if the hostname is an IPv6 address, strip the brackets around it to +++ // make it parse correctly +++ if (host.size() >= 2 && host.front() == '[' && host.back() == ']') +++ host = host.substr(1, host.size() - 2); +++ +++ int const port = host_end == std::string::npos || host_end <= 7 ? 80 +++ : atoi(req.req.substr(host_end + 1, path_start).c_str()); +++ assert(port >= 0 && port < 0xffff); +++ +++ bool found_host = false; +++ for (auto const& h : req.headers) +++ { +++ if (h.first == "host") found_host = true; +++ +++ out_request += h.first; +++ out_request += ": "; +++ out_request += h.second; +++ out_request += "\r\n"; +++ } +++ if (!found_host) +++ { +++ out_request += "host: "; +++ out_request += host; +++ out_request += "\r\n"; +++ } +++ out_request += "\r\n"; +++ +++ if (m_num_server_out_bytes + out_request.size() > sizeof(m_server_out_buffer)) +++ { +++ std::printf("http_proxy: Too many queued server requests: %d bytes\n" +++ , int(m_num_server_out_bytes + out_request.size())); +++ throw std::runtime_error("pipeline too deep"); +++ } +++ memmove(&m_server_out_buffer[m_num_server_out_bytes] +++ , out_request.data(), out_request.size()); +++ m_num_server_out_bytes += int(out_request.size()); +++ +++ if (!m_server_connection.is_open()) +++ { +++ boost::system::error_code err; +++ tcp::endpoint target(make_address(host.c_str(), err) +++ , static_cast(port)); +++ if (err) +++ { +++ char port_str[10]; +++ std::snprintf(port_str, sizeof(port_str), "%d", port); +++ m_resolver.async_resolve(host, port_str +++ , std::bind(&http_proxy::on_domain_lookup, this, _1, _2)); +++ return; +++ } +++ open_forward_connection(target); +++ return; +++ } +++ +++ // TODO: make sure we're connecting/connected to the same (host, port) +++ // that this request is for. Don't support multiple servers +++ +++ write_server_send_buffer(); +++ } +++ +++ void http_proxy::on_domain_lookup(boost::system::error_code const& ec +++ , const asio::ip::tcp::resolver::results_type ips) +++ { +++ if (ec || ips.empty()) +++ { +++ if (ec) +++ { +++ std::printf("http_proxy::on_domain_lookup: (%d) %s\n" +++ , ec.value(), ec.message().c_str()); +++ } +++ else +++ { +++ std::printf("http_proxy::on_request_domain_lookup: empty response\n"); +++ } +++ error(503, "Resource Temporarily Unavailable"); +++ return; +++ } +++ open_forward_connection(ips.front().endpoint()); +++ } +++ +++ void http_proxy::open_forward_connection(const asio::ip::tcp::endpoint& target) +++ { +++ m_server_connection.open(target.protocol()); +++ +++ std::printf("http_proxy: async_connect: %s:%d\n" +++ , target.address().to_string().c_str(), target.port()); +++ m_server_connection.async_connect(target +++ , std::bind(&http_proxy::on_connected, this, _1)); +++ } +++ +++ void http_proxy::error(int code, char const* message) +++ { +++ std::string send_buffer = send_response(code, message); +++ memcpy(m_in_buffer, send_buffer.data(), send_buffer.size()); +++ asio::async_write(m_client_connection, asio::buffer( +++ &m_in_buffer[0], send_buffer.size()) +++ , std::bind(&http_proxy::close_connection, this)); +++ } +++ +++ void http_proxy::on_connected(boost::system::error_code const& ec) +++ { +++ if (ec) +++ { +++ std::printf("http_proxy::on_connected() connection failed: %s\n", ec.message().c_str()); +++ m_server_connection.close(); +++ error(503, "Service Temporarily Unavailable"); +++ return; +++ } +++ +++ std::printf("http_proxy: connected\n"); +++ +++ write_server_send_buffer(); +++ +++ m_server_connection.async_read_some( +++ asio::buffer(m_in_buffer, sizeof(m_in_buffer)) +++ , std::bind(&http_proxy::on_server_receive, this, _1, _2)); +++ } +++ +++ void http_proxy::write_server_send_buffer() +++ { +++ if (m_writing_to_server) return; +++ m_writing_to_server = true; +++ m_server_connection.async_write_some(asio::buffer( +++ &m_server_out_buffer[0], m_num_server_out_bytes) +++ , std::bind(&http_proxy::on_server_write, this, _1, _2)); +++ } +++ +++ void http_proxy::on_server_write(error_code const& ec, size_t bytes_transferred) +++ { +++ m_writing_to_server = false; +++ if (ec) +++ { +++ std::printf("http_proxy::on_server_write: (%d) %s\n" +++ , ec.value(), ec.message().c_str()); +++ close_connection(); +++ return; +++ } +++ +++ memmove(&m_server_out_buffer[0], &m_server_out_buffer[bytes_transferred] +++ , m_num_server_out_bytes - bytes_transferred); +++ m_num_server_out_bytes -= int(bytes_transferred); +++ +++ if (m_num_server_out_bytes > 0) +++ write_server_send_buffer(); +++ } +++ +++ // we received some data from the server, forward it to the server +++ void http_proxy::on_server_receive(boost::system::error_code const& ec +++ , std::size_t bytes_transferred) +++ { +++ if (ec) +++ { +++ std::printf("http_proxy: error reading from server: (%d) %s\n" +++ , ec.value(), ec.message().c_str()); +++ close_connection(); +++ return; +++ } +++ +++ asio::async_write(m_client_connection, asio::buffer(&m_in_buffer[0], bytes_transferred) +++ , std::bind(&http_proxy::on_server_forward, this, _1, _2)); +++ } +++ +++ void http_proxy::on_server_forward(error_code const& ec +++ , size_t) +++ { +++ if (ec) +++ { +++ std::printf("http_proxy: error writing to client: (%d) %s\n" +++ , ec.value(), ec.message().c_str()); +++ close_connection(); +++ return; +++ } +++ +++ m_server_connection.async_read_some( +++ sim::asio::buffer(m_in_buffer, sizeof(m_in_buffer)) +++ , std::bind(&http_proxy::on_server_receive, this, _1, _2)); +++ } +++ +++ void http_proxy::stop() +++ { +++ m_close = true; +++ m_listen_socket.close(); +++ } +++ +++ void http_proxy::close_connection() +++ { +++ m_num_client_in_bytes = 0; +++ m_num_server_out_bytes = 0; +++ m_num_in_bytes = 0; +++ +++ error_code err; +++ m_client_connection.close(err); +++ if (err) +++ { +++ std::printf("http_proxy::close: failed to close client connection (%d) %s\n" +++ , err.value(), err.message().c_str()); +++ } +++ m_server_connection.close(err); +++ if (err) +++ { +++ std::printf("http_proxy::close: failed to close server connection (%d) %s\n" +++ , err.value(), err.message().c_str()); +++ } +++ +++ if (m_close) return; +++ +++ // now we can accept another connection +++ m_listen_socket.async_accept(m_client_connection, m_ep +++ , std::bind(&http_proxy::on_accept, this, _1)); +++ } +++} +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/http_server.cpp ++@@ -0,0 +1,425 @@ +++/* +++ +++Copyright (c) 2015, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#include "simulator/simulator.hpp" +++#include "simulator/http_server.hpp" +++ +++#include +++#include // for printf +++ +++using namespace sim::asio; +++using namespace sim::asio::ip; +++using namespace std::placeholders; +++ +++using boost::system::error_code; +++ +++namespace sim +++{ +++ using namespace aux; +++ +++ namespace { +++ char const* find(char const* hay, int const hsize +++ , char const* needle, int const nsize) +++ { +++ for (int i = 0; i < hsize - nsize + 1; ++i) +++ { +++ if (memcmp(hay + i, needle, nsize) == 0) return hay + i; +++ } +++ return nullptr; +++ } +++ } +++ +++ std::string trim(std::string s) +++ { +++ if (s.empty()) return s; +++ +++ int start = 0; +++ int end = int(s.size()); +++ while (strchr(" \r\n\t", s[start]) != NULL && start < end) +++ { +++ ++start; +++ } +++ +++ while (strchr(" \r\n\t", s[end-1]) != NULL && end > start) +++ { +++ --end; +++ } +++ return s.substr(start, end - start); +++ } +++ +++ std::string lower_case(std::string s) +++ { +++ std::string ret; +++ std::transform(s.begin(), s.end(), std::back_inserter(ret) +++ , [](char c) { return static_cast(tolower(c)); } ); +++ return ret; +++ } +++ +++ std::string normalize(const std::string& s) +++ { +++ std::vector elements; +++ char const* start = s.c_str(); +++ if (*start == '/') ++start; +++ char const* slash = strchr(start, '/'); +++ while (slash != NULL) +++ { +++ std::string element(start, slash - start); +++ if (element != "..") +++ { +++ elements.push_back(element); +++ } else if (!elements.empty()) +++ { +++ elements.erase(elements.end()-1); +++ } +++ start = slash + 1; +++ slash = strchr(start, '/'); +++ } +++ elements.push_back(start); +++ +++ std::string ret; +++ for (auto const& e : elements) +++ { +++ ret += '/'; +++ ret += e; +++ } +++ +++ return ret; +++ } +++ +++ // TODO: extra_header should be a std::vector +++ std::string send_response(int code, char const* status_message +++ , int len, char const** extra_header) +++ { +++ std::string ret = "HTTP/1.1 " + std::to_string(code) + " " + status_message + "\r\n"; +++ +++ ret += "content-length: " + std::to_string(len) + "\r\n"; +++ +++ if (extra_header) +++ { +++ ret += extra_header[0]; +++ ret += extra_header[1]; +++ ret += extra_header[2]; +++ ret += extra_header[3]; +++ } +++ ret += "\r\n"; +++ +++ return ret; +++ } +++ +++ http_server::http_server(io_context& ios, unsigned short listen_port, int flags) +++ : m_ios(ios) +++ , m_listen_socket(ios) +++ , m_connection(ios) +++ , m_bytes_used(0) +++ , m_close(false) +++ , m_flags(flags) +++ { +++ address local_ip = ios.get_ips().front(); +++ if (local_ip.is_v4()) +++ { +++ m_listen_socket.open(tcp::v4()); +++ m_listen_socket.bind(tcp::endpoint(address_v4::any(), listen_port)); +++ } +++ else +++ { +++ m_listen_socket.open(tcp::v6()); +++ m_listen_socket.bind(tcp::endpoint(address_v6::any(), listen_port)); +++ } +++ m_listen_socket.listen(); +++ +++ m_listen_socket.async_accept(m_connection, m_ep +++ , std::bind(&http_server::on_accept, this, _1)); +++ } +++ +++ void http_server::on_accept(error_code const& ec) +++ { +++ if (ec) +++ { +++ std::printf("http_server::on_accept: (%d) %s\n" +++ , ec.value(), ec.message().c_str()); +++ close_connection(); +++ return; +++ } +++ +++ ++m_accepted_connections; +++ +++ std::printf("http_server accepted connection from: %s : %d\n", +++ m_ep.address().to_string().c_str(), m_ep.port()); +++ +++ read(); +++ } +++ +++ void http_server::register_handler(std::string const& path, handler_t h) +++ { +++ m_handlers[path] = std::move(h); +++ } +++ +++ void http_server::register_content(std::string const& path +++ , std::int64_t const size, generator_t gen) +++ { +++ m_handlers[path] = [gen,size](std::string, std::string, std::map& hdr) +++ { +++ std::int64_t start = 0; +++ std::int64_t end = size; +++ +++ auto it = hdr.find("range"); +++ bool const range_req = it != hdr.end(); +++ if (range_req) +++ { +++ std::string range = it->second; +++ // skip "bytes " +++ range = range.substr(range.find_first_of('=') + 1); +++ start = std::stoll(range.substr(0, range.find('-'))); +++ end = std::stoll(range.substr(range.find_first_of('-') + 1)) + 1; +++ } +++ +++ std::string header = "Content-Range: bytes " + std::to_string(start) +++ + "-" + std::to_string(end-1) + "/" + std::to_string(end-start) + "\r\n"; +++ char const* extra_headers[4] = { header.c_str(), "", "", ""}; +++ +++ return sim::send_response(range_req ? 206 : 200 +++ , range_req ? "Partial Content" : "OK" +++ , int(end - start), range_req ? extra_headers : nullptr) +++ + gen(start, end - start); +++ }; +++ } +++ +++ void http_server::register_redirect(std::string const& path, std::string const& target) +++ { +++ m_handlers[path] = [target](std::string, std::string, std::map&) +++ { +++ std::string header = "Location: " + target + "\r\n"; +++ char const* extra_headers[4] = { header.c_str(), "", "", ""}; +++ return sim::send_response(301, "Moved Permanently", 0, extra_headers); +++ }; +++ } +++ +++ void http_server::register_stall_handler(std::string const& path){ +++ m_stall_handlers.insert(path); +++ } +++ +++ void http_server::read() +++ { +++ if (m_bytes_used >= int(m_recv_buffer.size()) / 2) +++ { +++ m_recv_buffer.resize((std::max)(500, m_bytes_used * 2)); +++ } +++ assert(int(m_recv_buffer.size()) > m_bytes_used); +++ m_connection.async_read_some(asio::buffer(&m_recv_buffer[m_bytes_used] +++ , m_recv_buffer.size() - m_bytes_used) +++ , std::bind(&http_server::on_read, this, _1, _2)); +++ } +++ +++ http_request parse_request(char const* start, int len) +++ { +++ http_request ret; +++ +++ char const* const end_of_request = start + len; +++ char const* const space = find(start, len, " ", 1); +++ if (space == nullptr) +++ { +++ std::printf("http_server: failed to parse request:\n%s\n" +++ , std::string(start, len).c_str()); +++ throw std::runtime_error("parse failed"); +++ } +++ +++ char const* const space2 = find(space + 1 +++ , int(len - (space - start + 1)), " ", 1); +++ if (space2 == nullptr) +++ { +++ std::printf("http_server: failed to parse request:\n%s\n" +++ , std::string(start, len).c_str()); +++ throw std::runtime_error("parse failed"); +++ } +++ ret.method.assign(start, space); +++ ret.req.assign(space+1, space2); +++ if (ret.method != "CONNECT") { +++ ret.path.assign(normalize(ret.req.substr(0, ret.req.find_first_of('?')))); +++ } else { +++ ret.path.assign(ret.req); +++ } +++ std::printf("parse_request: %s %s [%s]\n" +++ , ret.method.c_str(), ret.path.c_str(), ret.req.c_str()); +++ +++ char const* header = find(space2, int(len - (space2 - start)), "\r\n", 2); +++ while (header != end_of_request - 4) +++ { +++ if (header == nullptr) +++ { +++ std::printf("http_server: failed to parse request:\n%s\n" +++ , std::string(start, len).c_str()); +++ throw std::runtime_error("parse failed"); +++ } +++ char const* const next = find(header + 2 +++ , int(len - (header + 2 - start)), "\r\n", 2); +++ char const* const value = static_cast(memchr(header, ':', len - (header - start))); +++ if (value == nullptr || next == nullptr || value > next) +++ { +++ std::printf("http_server: failed to parse request:\n%s\n" +++ , std::string(start, len).c_str()); +++ throw std::runtime_error("parse failed"); +++ } +++ +++ ret.headers[lower_case(trim(std::string(header, value)))] +++ = trim(std::string(value+1, next)); +++ +++ header = next; +++ } +++ return ret; +++ } +++ +++ int find_request_len(char const* buf, int const len) +++ { +++ char const* end_of_request = find(buf, len, "\r\n\r\n", 4); +++ if (end_of_request == nullptr) return -1; +++ return int(end_of_request - buf + 4); +++ } +++ +++ void http_server::on_read(error_code const& ec, size_t bytes_transferred) try +++ { +++ if (ec) +++ { +++ std::printf("http_server::on_read: (%d) %s\n" +++ , ec.value(), ec.message().c_str()); +++ close_connection(); +++ return; +++ } +++ +++ m_bytes_used += int(bytes_transferred); +++ +++ int const req_len = find_request_len(m_recv_buffer.data(), m_bytes_used); +++ if (req_len < 0) +++ { +++ read(); +++ return; +++ } +++ +++ http_request req = parse_request(m_recv_buffer.data(), req_len); +++ +++ m_recv_buffer.erase(m_recv_buffer.begin(), m_recv_buffer.begin() + req_len); +++ m_bytes_used -= req_len; +++ +++ auto it = m_handlers.find(req.path); +++ if (it == m_handlers.end()) +++ { +++ if (m_stall_handlers.find(req.path) != m_stall_handlers.end()) +++ { +++ return; +++ } +++ // no handler found, 404 +++ m_send_buffer = send_response(404, "Not Found"); +++ } +++ else +++ { +++ m_send_buffer = it->second(req.method, req.req, req.headers); +++ } +++ +++ // decide whether to close the connection after this response, and signal +++ // it to the client appropriately. +++ bool close; +++ if (m_flags & http_1_0) +++ { +++ // an HTTP/1.0 server closes after every response and does not use the +++ // Connection header (that is an HTTP/1.1 mechanism). Downgrade the +++ // status line so the client detects this from the protocol version. +++ close = true; +++ auto const ver = m_send_buffer.find("HTTP/1.1"); +++ if (ver != std::string::npos) +++ m_send_buffer.replace(ver, 8, "HTTP/1.0"); +++ } +++ else +++ { +++ // close if the client asked us to, or if this server is not +++ // configured for keep-alive. When we do, advertise it with a +++ // "Connection: close" response header so the client knows not to +++ // reuse the socket (rather than discovering it via a failed write). +++ close = lower_case(req.headers["connection"]) == "close" +++ || !(m_flags & keep_alive); +++ if (close) +++ { +++ auto const status_end = m_send_buffer.find("\r\n"); +++ if (status_end != std::string::npos) +++ { +++ assert(m_send_buffer.find("Connection:") == std::string::npos); +++ m_send_buffer.insert(status_end + 2, "Connection: close\r\n"); +++ } +++ } +++ } +++ +++ async_write(m_connection, asio::buffer(m_send_buffer.data() +++ , m_send_buffer.size()), std::bind(&http_server::on_write +++ , this, _1, _2, close)); +++ } +++ catch (std::exception& e) +++ { +++ std::printf("http_server::on_read() failed: %s\n" +++ , e.what()); +++ close_connection(); +++ } +++ +++ void http_server::on_write(error_code const& ec +++ , size_t /* bytes_transferred */ +++ , bool close) +++ { +++ if (ec) +++ { +++ std::printf("http_server::on_write: (%d) %s\n" +++ , ec.value(), ec.message().c_str()); +++ close_connection(); +++ return; +++ } +++ +++ if (!close) +++ { +++ // try to read another request out of the buffer +++ post(m_ios, std::bind(&http_server::on_read, this, error_code(), 0)); +++ } +++ else +++ { +++ close_connection(); +++ } +++ } +++ +++ void http_server::stop() +++ { +++ m_close = true; +++ m_listen_socket.close(); +++ } +++ +++ void http_server::close_connection() +++ { +++ m_recv_buffer.clear(); +++ m_bytes_used = 0; +++ +++ error_code err; +++ m_connection.close(err); +++ if (err) +++ { +++ std::printf("http_server::close: failed to close connection (%d) %s\n" +++ , err.value(), err.message().c_str()); +++ return; +++ } +++ +++ if (m_close) return; +++ +++ // now we can accept another connection +++ m_listen_socket.async_accept(m_connection, m_ep +++ , std::bind(&http_server::on_accept, this, _1)); +++ } +++} +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/io_service.cpp ++@@ -0,0 +1,272 @@ +++/* +++ +++Copyright (c) 2015, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#include "simulator/simulator.hpp" +++ +++#include +++#include +++ +++namespace sim { namespace asio { +++ +++ io_context::io_context(sim::simulation& sim) +++ : io_context(sim, std::vector()) +++ {} +++ +++ io_context::io_context(sim::simulation& sim, asio::ip::address const& ip) +++ : io_context(sim, std::vector{ip}) +++ {} +++ +++ io_context::io_context(sim::simulation& sim, std::vector const& ips) +++ : m_sim(sim) +++ , m_ips(ips) +++ , m_stopped(false) +++ { +++ for (auto const& ip : m_ips) +++ { +++ m_outgoing_route[ip] = m_sim.config().outgoing_route(ip); +++ m_incoming_route[ip] = m_sim.config().incoming_route(ip); +++ } +++ m_sim.add_io_service(this); +++ } +++ +++ io_context::~io_context() +++ { +++ m_sim.remove_io_service(this); +++ } +++ +++ // this constructor is never meant to be called (hence the assert). It only +++ // exists to satisfy interfaces that require a default-like constructor. The +++ // null reference is deliberate, so suppress the warning about it. +++#if defined __clang__ +++#pragma clang diagnostic push +++#pragma clang diagnostic ignored "-Wnull-dereference" +++#elif defined __GNUC__ +++#pragma GCC diagnostic push +++#pragma GCC diagnostic ignored "-Wnull-dereference" +++#endif +++ io_context::io_context(std::size_t) +++ : m_sim(*reinterpret_cast(0)) +++ { +++ assert(false); +++ } +++#if defined __clang__ +++#pragma clang diagnostic pop +++#elif defined __GNUC__ +++#pragma GCC diagnostic pop +++#endif +++ +++ int io_context::get_path_mtu(const asio::ip::address& source, const asio::ip::address& dest) const +++ { +++ // TODO: it would be nice to actually traverse the virtual network nodes +++ // and ask for their MTU instead +++ assert(std::count(m_ips.begin(), m_ips.end(), source) > 0 && "source address must be a local address to this node/io_context"); +++ return m_sim.config().path_mtu(source, dest); +++ } +++ +++ void io_context::stop() +++ { +++ // TODO: cancel all outstanding handler associated with this io_context +++ m_stopped = true; +++ } +++ +++ bool io_context::stopped() const +++ { +++ return m_stopped; +++ } +++ +++ void io_context::restart() +++ { +++ m_stopped = false; +++ } +++ +++ std::size_t io_context::run() +++ { +++ assert(false); +++ return 0; +++ } +++ +++ std::size_t io_context::run(boost::system::error_code&) +++ { +++ assert(false); +++ return 0; +++ } +++ +++ std::size_t io_context::poll() +++ { +++ assert(false); +++ return 0; +++ } +++ +++ std::size_t io_context::poll(boost::system::error_code&) +++ { +++ assert(false); +++ return 0; +++ } +++ +++ std::size_t io_context::poll_one() +++ { +++ assert(false); +++ return 0; +++ } +++ +++ std::size_t io_context::poll_one(boost::system::error_code&) +++ { +++ assert(false); +++ return 0; +++ } +++ +++ // private interface +++ +++ void io_context::add_timer(high_resolution_timer* t) +++ { +++ m_sim.add_timer(t); +++ } +++ +++ void io_context::remove_timer(high_resolution_timer* t) +++ { +++ m_sim.remove_timer(t); +++ } +++ +++ boost::asio::io_context& io_context::get_internal_service() +++ { return m_sim.get_internal_service(); } +++ +++ ip::tcp::endpoint io_context::bind_socket(ip::tcp::socket* socket +++ , ip::tcp::endpoint ep, boost::system::error_code& ec) +++ { +++ assert(!m_ips.empty() && "you cannot use an internal io_context (one without an IP address) for creating and binding sockets"); +++ if (ep.address() == ip::address_v4::any()) +++ { +++ auto it = std::find_if(m_ips.begin(), m_ips.end() +++ , [](ip::address const& ip) { return ip.is_v4(); } ); +++ if (it == m_ips.end()) +++ { +++ ec.assign(boost::system::errc::address_not_available +++ , boost::system::generic_category()); +++ return ip::tcp::endpoint(); +++ } +++ // TODO: pick the first local endpoint for now. In the future we may +++ // want have a bias toward +++ ep.address(*it); +++ } +++ else if (ep.address() == ip::address_v6::any()) +++ { +++ auto it = std::find_if(m_ips.begin(), m_ips.end() +++ , [](ip::address const& ip) { return ip.is_v6(); } ); +++ if (it == m_ips.end()) +++ { +++ ec.assign(boost::system::errc::address_not_available +++ , boost::system::generic_category()); +++ return ip::tcp::endpoint(); +++ } +++ // TODO: pick the first local endpoint for now. In the future we may +++ // want have a bias toward +++ ep.address(*it); +++ } +++ else if (std::count(m_ips.begin(), m_ips.end(), ep.address()) == 0) +++ { +++ // you can only bind to the IP assigned to this node. +++ // TODO: support loopback +++ ec.assign(boost::system::errc::address_not_available +++ , boost::system::generic_category()); +++ return ip::tcp::endpoint(); +++ } +++ +++ return m_sim.bind_socket(socket, ep, ec); +++ } +++ +++ void io_context::unbind_socket(ip::tcp::socket* socket +++ , const ip::tcp::endpoint& ep) +++ { +++ m_sim.unbind_socket(socket, ep); +++ } +++ +++ void io_context::rebind_socket(asio::ip::tcp::socket* prev, asio::ip::tcp::socket* s, asio::ip::tcp::endpoint ep) +++ { +++ m_sim.rebind_socket(prev, s, ep); +++ } +++ +++ ip::udp::endpoint io_context::bind_udp_socket(ip::udp::socket* socket +++ , ip::udp::endpoint ep, boost::system::error_code& ec) +++ { +++ assert(!m_ips.empty() && "you cannot use an internal io_context (one without an IP address) for creating and binding sockets"); +++ if (ep.address() == ip::address_v4::any()) +++ { +++ auto it = std::find_if(m_ips.begin(), m_ips.end() +++ , [](ip::address const& ip) { return ip.is_v4(); }); +++ if (it == m_ips.end()) +++ { +++ ec.assign(boost::system::errc::address_not_available +++ , boost::system::generic_category()); +++ return ip::udp::endpoint(); +++ } +++ // TODO: pick the first local endpoint for now. In the future we may +++ // want have a bias toward +++ ep.address(*it); +++ } +++ else if (ep.address() == ip::address_v6::any()) +++ { +++ auto it = std::find_if(m_ips.begin(), m_ips.end() +++ , [](ip::address const& ip) { return ip.is_v6(); }); +++ if (it == m_ips.end()) +++ { +++ ec.assign(boost::system::errc::address_not_available +++ , boost::system::generic_category()); +++ return ip::udp::endpoint(); +++ } +++ // TODO: pick the first local endpoint for now. In the future we may +++ // want have a bias toward +++ ep.address(*it); +++ } +++ else if (std::count(m_ips.begin(), m_ips.end(), ep.address()) == 0) +++ { +++ // you can only bind to the IP assigned to this node. +++ // TODO: support loopback +++ ec.assign(boost::system::errc::address_not_available +++ , boost::system::generic_category()); +++ return ip::udp::endpoint(); +++ } +++ +++ return m_sim.bind_udp_socket(socket, ep, ec); +++ } +++ +++ void io_context::unbind_udp_socket(ip::udp::socket* socket +++ , const ip::udp::endpoint& ep) +++ { +++ m_sim.unbind_udp_socket(socket, ep); +++ } +++ +++ void io_context::rebind_udp_socket(asio::ip::udp::socket* socket, asio::ip::udp::endpoint ep) +++ { +++ m_sim.rebind_udp_socket(socket, ep); +++ } +++ +++ std::shared_ptr io_context::internal_connect(ip::tcp::socket* s +++ , ip::tcp::endpoint const& target, boost::system::error_code& ec) +++ { +++ return m_sim.internal_connect(s, target, ec); +++ } +++ +++ route io_context::find_udp_socket(asio::ip::udp::socket const& socket +++ , ip::udp::endpoint const& ep) +++ { +++ return m_sim.find_udp_socket(socket, ep); +++ } +++ +++} // asio +++} // sim +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/nat.cpp ++@@ -0,0 +1,47 @@ +++/* +++ +++Copyright (c) 2018, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#include "simulator/nat.hpp" +++#include "simulator/simulator.hpp" +++#include "simulator/packet.hpp" +++#include +++ +++namespace sim { +++ +++ nat::nat(asio::ip::address external_addr) : m_external_addr(external_addr) {} +++ +++ void nat::incoming_packet(aux::packet p) +++ { +++ // unconditionally replacing the "from" address is fine because of our +++ // simplified network model where the two paths of a connection are set up +++ // independently, and we can set up the nat hop only on the outgoing path +++ p.from.address(m_external_addr); +++ if (p.channel) { +++ p.channel->visible_ep[0].address(m_external_addr); +++ } +++ forward_packet(std::move(p)); +++ } +++ +++ // used for visualization +++ std::string nat::label() const +++ { +++ return std::string("NAT [") + m_external_addr.to_string() + "]"; +++ } +++ +++} // sim +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/pcap.cpp ++@@ -0,0 +1,204 @@ +++/* +++ +++Copyright (c) 2017, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#include "simulator/pcap.hpp" +++#include "simulator/chrono.hpp" +++#include "simulator/packet.hpp" +++ +++using sim::chrono::duration_cast; +++using sim::chrono::seconds; +++ +++using sim::asio::ip::address_v4; +++using sim::asio::ip::tcp; +++using sim::asio::ip::udp; +++ +++namespace sim { namespace aux { +++ +++namespace { +++ +++ template ::value>::type> +++ void write(std::fstream& o, T const& value) +++ { o.write(reinterpret_cast(&value), sizeof(value)); } +++ +++ // documented here: +++ // http://www.tcpdump.org/linktypes.html +++ std::uint32_t const LINKTYPE_RAW = 101; +++ +++ struct ip_header +++ { +++ std::uint8_t version_len; +++ std::uint8_t dscp; +++ std::uint16_t length; +++ std::uint16_t identification; +++ std::uint16_t fragment; +++ std::uint8_t ttl; +++ std::uint8_t protocol; +++ std::uint16_t checksum; +++ std::uint32_t source_ip; +++ std::uint32_t destination_ip; +++ }; +++ +++ struct udp_header +++ { +++ std::uint16_t src_port; +++ std::uint16_t dst_port; +++ std::uint16_t length; +++ std::uint16_t checksum; +++ }; +++ +++ struct tcp_header +++ { +++ std::uint16_t src_port; +++ std::uint16_t dst_port; +++ std::uint32_t seq_nr; +++ std::uint32_t ack_nr; +++ std::uint8_t offset; +++ std::uint8_t flags; +++ std::uint16_t window_size; +++ std::uint16_t checksum; +++ std::uint16_t urgent; +++ }; +++} +++ +++ pcap::pcap(char const* filename) +++ : m_file(filename, std::ios_base::out | std::ios_base::trunc | std::ios_base::binary) +++ { +++ // file format documented here: +++ // https://wiki.wireshark.org/Development/LibpcapFileFormat +++ // write pcap file header +++ write(m_file, std::uint32_t(0xa1b2c3d4)); // magic number +++ write(m_file, std::uint16_t(2)); // versopm major +++ write(m_file, std::uint16_t(4)); // versopm minor +++ write(m_file, std::int32_t(0)); // thiszone +++ write(m_file, std::uint32_t(0)); // sigfigs +++ write(m_file, std::uint32_t(0xffff)); // snaplen +++ write(m_file, LINKTYPE_RAW); // network +++ } +++ +++ void write_ip_header(std::fstream& file, int const size, int const protocol +++ , address_v4 const source, address_v4 const dest) +++ { +++ ip_header const header = { +++ (4 << 4) | 5, // version_len +++ 0, // dscp +++ htons(std::uint16_t(size)), // length +++ 0, // identification +++ 0, // fragment +++ 200, // ttl +++ std::uint8_t(protocol), // protocol +++ 0, // checksum +++ htonl(std::uint32_t(source.to_uint())), // source ip +++ htonl(std::uint32_t(dest.to_uint())) // destination ip +++ }; +++ +++ write(file, header); +++ } +++ +++ void write_udp_header(std::fstream& file, int const size, int const src_port +++ , int const dst_port) +++ { +++ assert(src_port != 0); +++ assert(dst_port != 0); +++ udp_header const header = { +++ htons(std::uint16_t(src_port)), // source port +++ htons(std::uint16_t(dst_port)), // destination port +++ htons(std::uint16_t(sizeof(udp_header) + size)), // length +++ 0 // checksum +++ }; +++ +++ write(file, header); +++ } +++ +++ void write_tcp_header(std::fstream& file, int const src_port +++ , int const dst_port, std::uint32_t const seq_nr) +++ { +++ assert(src_port != 0); +++ assert(dst_port != 0); +++ tcp_header const header = { +++ htons(std::uint16_t(src_port)), // source port +++ htons(std::uint16_t(dst_port)), // destination port +++ htonl(seq_nr), // sequence number +++ std::uint32_t(0), // acknowledgement number +++ std::uint8_t(5 << 4), // header size (data offset) +++ std::uint8_t(0), // flags +++ htons(std::uint16_t(65535)), // window size +++ std::uint16_t(0), // checksum +++ std::uint16_t(0) // urgent offset +++ }; +++ +++ write(file, header); +++ } +++ +++ void pcap::log_tcp(packet const& p, tcp::endpoint const src +++ , tcp::endpoint const dst) +++ { +++ // synthesize IP/TCP header and write packet +++ auto const now = chrono::high_resolution_clock::now(); +++ +++ // just an arbitrary posix time used as starting point +++ std::uint32_t const sim_start_time = 441794304; +++ std::uint32_t const secs = static_cast(duration_cast(now.time_since_epoch()).count()); +++ std::uint32_t const usecs = static_cast(duration_cast(now.time_since_epoch() - seconds(secs)).count()); +++ +++ std::uint32_t const packet_size = static_cast(sizeof(ip_header) + sizeof(tcp_header) + p.buffer.size()); +++ +++ write(m_file, sim_start_time + secs); +++ write(m_file, usecs); +++ write(m_file, packet_size); +++ write(m_file, packet_size); +++ +++ // 6 is the protocol number for TCP +++ write_ip_header(m_file, packet_size, 6 +++ , src.address().to_v4(), dst.address().to_v4()); +++ +++ // TODO: if the packet has error set with asio::error::eof +++ // set the FIN flag +++ write_tcp_header(m_file, p.from.port(), dst.port(), p.byte_counter); +++ +++ m_file.write(reinterpret_cast(p.buffer.data()), p.buffer.size()); +++ } +++ +++ void pcap::log_udp(packet const& p, udp::endpoint const src +++ , udp::endpoint const dst) +++ { +++ // synthesize IP/UDP header and write packet +++ auto const now = chrono::high_resolution_clock::now(); +++ +++ // just an arbitrary posix time used as starting point +++ std::uint32_t const sim_start_time = 441794304; +++ std::uint32_t const secs = static_cast(duration_cast(now.time_since_epoch()).count()); +++ std::uint32_t const usecs = static_cast(duration_cast(now.time_since_epoch() - seconds(secs)).count()); +++ +++ std::uint32_t const packet_size = static_cast(sizeof(ip_header) + sizeof(udp_header) + p.buffer.size()); +++ +++ write(m_file, sim_start_time + secs); +++ write(m_file, usecs); +++ write(m_file, packet_size); +++ write(m_file, packet_size); +++ +++ // 17 is the protocol number for UDP +++ write_ip_header(m_file, packet_size, 17 +++ , src.address().to_v4(), dst.address().to_v4()); +++ +++ write_udp_header(m_file, static_cast(p.buffer.size()), p.from.port(), static_cast(dst.port())); +++ +++ m_file.write(reinterpret_cast(p.buffer.data()), p.buffer.size()); +++ } +++ +++}} +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/queue.cpp ++@@ -0,0 +1,142 @@ +++/* +++ +++Copyright (c) 2015, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#include "simulator/queue.hpp" +++#include "simulator/handler_allocator.hpp" +++#include +++#include // for printf +++ +++typedef sim::chrono::high_resolution_clock::time_point time_point; +++typedef sim::chrono::high_resolution_clock::duration duration; +++ +++namespace sim +++{ +++ using namespace aux; +++ +++ queue::queue(asio::io_context& ios +++ , int bandwidth +++ , chrono::high_resolution_clock::duration propagation_delay +++ , int max_queue_size +++ , std::string name) +++ : m_max_queue_size(max_queue_size) +++ , m_forwarding_latency(propagation_delay) +++ , m_bandwidth(bandwidth) +++ , m_queue_size(0) +++ , m_node_name(name) +++ , m_forward_timer(ios) +++ , m_last_forward(chrono::high_resolution_clock::now()) +++ {} +++ +++ std::string queue::label() const +++ { +++ char ret[400]; +++ int p = std::snprintf(ret, sizeof(ret), "%s\n", m_node_name.c_str()); +++ +++ if (m_bandwidth != 0) +++ { +++ p += std::snprintf(ret + p, sizeof(ret) - p, "rate: %d kB/s\n" +++ , m_bandwidth / 1000); +++ } +++ +++ if (m_queue_size != 0) +++ { +++ p += std::snprintf(ret + p, sizeof(ret) - p, "queue: %d kB\n" +++ , m_queue_size / 1000); +++ } +++ +++ if (m_forwarding_latency.count() != 0) +++ { +++ p += std::snprintf(ret + p, sizeof(ret) - p, "latency: %d ms\n" +++ , int(chrono::duration_cast(m_forwarding_latency).count())); +++ } +++ +++ return ret; +++ } +++ +++ void queue::incoming_packet(aux::packet p) +++ { +++ const int packet_size = int(p.buffer.size() + p.overhead); +++ +++ // tail-drop +++ if (p.ok_to_drop() +++ && m_max_queue_size > 0 +++ && m_queue_size + packet_size > m_max_queue_size) +++ { +++ // if any hop on the network drops a packet, it has to return it to the +++ // sender. +++ auto drop_fun = std::move(p.drop_fun); +++ if (drop_fun) drop_fun(std::move(p)); +++ return; +++ } +++ +++ time_point const now = chrono::high_resolution_clock::now(); +++ +++ m_queue.emplace_back(now + m_forwarding_latency, std::move(p)); +++ m_queue_size += packet_size; +++ if (m_queue.size() > 1) return; +++ +++ begin_send_next_packet(); +++ } +++ +++ void queue::begin_send_next_packet() +++ { +++ time_point now = chrono::high_resolution_clock::now(); +++ +++ if (m_queue.front().ts > now) +++ { +++ m_forward_timer.expires_at(m_queue.front().ts); +++ m_forward_timer.async_wait(make_malloc(std::bind(&queue::begin_send_next_packet +++ , this))); +++ return; +++ } +++ +++ m_last_forward = now; +++ if (m_bandwidth == 0) +++ { +++ post(m_forward_timer.get_executor(), make_malloc(std::bind(&queue::next_packet_sent +++ , this))); +++ return; +++ } +++ const double nanoseconds_per_byte = 1000000000.0 +++ / double(m_bandwidth); +++ +++ aux::packet const& p = m_queue.front().pkt; +++ const int packet_size = int(p.buffer.size() + p.overhead); +++ +++ m_last_forward += chrono::duration_cast(chrono::nanoseconds( +++ boost::int64_t(nanoseconds_per_byte * packet_size))); +++ +++ m_forward_timer.expires_at(m_last_forward); +++ m_forward_timer.async_wait(make_malloc(std::bind(&queue::next_packet_sent +++ , this))); +++ } +++ +++ void queue::next_packet_sent() +++ { +++ aux::packet p = std::move(m_queue.front().pkt); +++ m_queue.erase(m_queue.begin()); +++ const int packet_size = int(p.buffer.size() + p.overhead); +++ m_queue_size -= packet_size; +++ +++ forward_packet(std::move(p)); +++ +++ if (m_queue.size()) +++ begin_send_next_packet(); +++ } +++} +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/resolver.cpp ++@@ -0,0 +1,143 @@ +++/* +++ +++Copyright (c) 2015, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#include "simulator/simulator.hpp" +++#include "simulator/handler_allocator.hpp" +++#include +++ +++typedef sim::chrono::high_resolution_clock::time_point time_point; +++typedef sim::chrono::high_resolution_clock::duration duration; +++ +++using namespace std::placeholders; +++ +++namespace sim { +++namespace asio { +++namespace ip { +++ +++ template +++ basic_resolver::basic_resolver(io_context& ios) +++ : m_ios(&ios) +++ , m_timer(ios) +++ {} +++ +++ template +++ basic_resolver::basic_resolver(basic_resolver&&) noexcept = default; +++ +++ template +++ basic_resolver& basic_resolver::operator=(basic_resolver&&) noexcept = default; +++ +++ template +++ void basic_resolver::async_resolve(std::string hostname, char const* service +++ , aux::function handler) +++ { +++ std::vector result; +++ boost::system::error_code ec; +++ +++ const chrono::high_resolution_clock::time_point start_time = +++ m_queue.empty() ? chrono::high_resolution_clock::now() : +++ m_queue.front().completion_time; +++ +++ assert(!m_ios->get_ips().empty() && "internal io service objects can only " +++ "be used for timers"); +++ +++ // if the hostname is an IP address, resolve it immediately +++ asio::ip::address addr = make_address_v4(hostname, ec); +++ if (ec) addr = make_address_v6(hostname, ec); +++ if (!ec) +++ { +++ const chrono::high_resolution_clock::time_point t = chrono::high_resolution_clock::now() +++ + chrono::microseconds(1); +++ results_type ips; +++ int const port = atoi(service); +++ assert(port >= 0 && port <= 0xffff); +++ ips.emplace_back( +++ typename Protocol::endpoint(addr, static_cast(port)) +++ , hostname, service); +++ result_t res{t, ec, std::move(ips), std::move(handler) }; +++ m_queue.insert(m_queue.begin(), std::move(res)); +++ m_timer.expires_at(m_queue.front().completion_time); +++ m_timer.async_wait(aux::make_malloc(std::bind(&basic_resolver::on_lookup, this, _1))); +++ return; +++ } +++ ec.clear(); +++ +++ const chrono::high_resolution_clock::time_point completion_time = +++ start_time +++ + m_ios->sim().config().hostname_lookup(m_ios->get_ips().front(), hostname +++ , result, ec); +++ +++ results_type ips; +++ +++ int const port = atoi(service); +++ assert(port >= 0 && port <= 0xffff); +++ +++ for (auto const& ip : result) +++ { +++ ips.emplace_back( +++ typename Protocol::endpoint(ip, static_cast(port)) +++ , hostname, service); +++ } +++ +++ result_t res{ completion_time, ec, std::move(ips), std::move(handler)}; +++ m_queue.emplace_back(std::move(res)); +++ +++ m_timer.expires_at(m_queue.front().completion_time); +++ m_timer.async_wait(aux::make_malloc(std::bind(&basic_resolver::on_lookup, this, _1))); +++ } +++ +++ template +++ void basic_resolver::on_lookup(boost::system::error_code const& ec) +++ { +++ if (ec == asio::error::operation_aborted) return; +++ +++ if (m_queue.empty()) return; +++ +++ typename queue_t::value_type v = std::move(m_queue.front()); +++ m_queue.erase(m_queue.begin()); +++ +++ // once the handler is called, it's possible the last reference keeping +++ // this object (basic_resolver) alive is released and we're deleted. Make +++ // sure to not touch any members after the handler in that case. +++ bool const empty = m_queue.empty(); +++ v.handler(v.err, std::move(v.ips)); +++ if (empty) return; +++ +++ m_timer.expires_at(m_queue.front().completion_time); +++ m_timer.async_wait(aux::make_malloc(std::bind(&basic_resolver::on_lookup, this, _1))); +++ } +++ +++ template +++ void basic_resolver::cancel() +++ { +++ queue_t q; +++ m_queue.swap(q); +++ for (auto& r : q) +++ { +++ r.err = asio::error::operation_aborted; +++ post(m_timer.get_executor(), aux::make_malloc(std::bind(std::move(r.handler) +++ , r.err, std::move(r.ips)))); +++ } +++ } +++ +++ // explicitly instantiate the functions +++ template struct basic_resolver; +++ template struct basic_resolver; +++} +++} +++} +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/simulation.cpp ++@@ -0,0 +1,381 @@ +++/* +++ +++Copyright (c) 2015, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#include "simulator/simulator.hpp" +++#include "simulator/packet.hpp" +++#include "simulator/pcap.hpp" +++ +++#include // for tie +++#include // for make_shared +++#include // for printf +++#include +++#include // for std::runtime_error +++ +++using namespace sim::asio; +++ +++namespace sim +++{ +++ simulation::simulation(configuration& config) +++ : m_config(config) +++ , m_internal_ios(new asio::io_context(*this)) +++ , m_service(1) +++ { +++ sim::chrono::reset_clock(); +++ m_config.build(*this); +++ } +++ +++ simulation::~simulation() +++ { +++ m_config.clear(); +++ +++ assert(m_timer_queue.empty()); +++ } +++ +++ std::size_t simulation::run() try +++ { +++ std::size_t ret = 0; +++ std::size_t last_executed = 0; +++ +++ // Detect a runaway loop where events keep firing but simulated time +++ // never advances - typically a timer being repeatedly scheduled at the +++ // current time (a 100% CPU spin that would otherwise hang run() +++ // forever). Legitimate bursts of same-timestamp events are finite, so +++ // a large threshold reliably distinguishes them from an unbounded spin. +++ chrono::high_resolution_clock::time_point prev_time +++ = chrono::high_resolution_clock::now(); +++ std::int64_t stalled_iterations = 0; +++ constexpr std::int64_t max_stalled_iterations = 1000; +++ +++ do { +++ +++ m_service.restart(); +++ last_executed = m_service.poll(); +++ ret += last_executed; +++ +++ chrono::high_resolution_clock::time_point now +++ = chrono::high_resolution_clock::now(); +++ +++ std::lock_guard l(m_timer_queue_mutex); +++ if (!m_timer_queue.empty()) { +++ asio::high_resolution_timer* next_timer = *m_timer_queue.begin(); +++ chrono::high_resolution_clock::fast_forward(next_timer->expiry() - now); +++ +++ now = chrono::high_resolution_clock::now(); +++ +++ while (!m_timer_queue.empty() +++ && (*m_timer_queue.begin())->expiry() <= now) { +++ +++ next_timer = *m_timer_queue.begin(); +++ m_timer_queue.erase(m_timer_queue.begin()); +++ next_timer->fire(boost::system::error_code()); +++ ++last_executed; +++ ++ret; +++ } +++ } +++ +++ if (last_executed > 0) { +++ if (now == prev_time) { +++ if (++stalled_iterations > max_stalled_iterations) { +++ throw std::runtime_error("libsimulator: simulated time " +++ "has not advanced for too many consecutive iterations " +++ "while events keep firing. The simulation is stuck in a " +++ "busy loop, typically a timer repeatedly scheduled at " +++ "the current time (a 100% CPU spin)."); +++ } +++ } else { +++ stalled_iterations = 0; +++ prev_time = now; +++ } +++ } +++ +++// std::fprintf(stderr, "run: last_executed: %d stopped: %d timer-queue: %d\n" +++// , int(last_executed), m_stopped, int(m_timer_queue.size())); +++ } while (last_executed > 0 && !m_stopped); +++ +++// std::fprintf(stderr, "exiting simulation::run(): last_executed: %d stopped: %d timer-queue: %d ret: %d\n" +++// , int(last_executed), m_stopped, int(m_timer_queue.size()), int(ret)); +++ return ret; +++ } +++ catch (...) +++ { +++ // cancel all outstanding timers +++ // we make a copy, since cancelling the timers will mutate m_timer_queue +++ auto queue = m_timer_queue; +++ for (auto& t : queue) +++ t->cancel(); +++ +++ auto listen_sockets = m_listen_sockets; +++ for (auto& s : listen_sockets) +++ s.second->cancel(); +++ +++ auto udp_sockets = m_udp_sockets; +++ for (auto& s : udp_sockets) +++ s.second->cancel(); +++ m_stopped = true; +++ throw; +++ } +++ +++ void simulation::stop() { m_stopped = true; } +++ bool simulation::stopped() const { return m_stopped; } +++ void simulation::restart() { m_stopped = false; } +++ +++ void simulation::add_timer(asio::high_resolution_timer* t) +++ { +++ assert(!m_stopped); +++ if (t->expiry() == sim::chrono::high_resolution_clock::now()) +++ { +++ std::fprintf(stderr, "WARNING: timer scheduled for current time!\n"); +++ } +++ std::lock_guard l(m_timer_queue_mutex); +++ // make sure we get a deterministic ordering of timers with the same +++ // expiration time +++ auto it = std::upper_bound(m_timer_queue.begin(), m_timer_queue.end(), t, timer_compare()); +++ m_timer_queue.insert(it, t); +++ } +++ +++ void simulation::remove_timer(asio::high_resolution_timer* t) +++ { +++ std::lock_guard l(m_timer_queue_mutex); +++ if (m_timer_queue.empty()) return; +++ timer_queue_t::iterator begin; +++ timer_queue_t::iterator end; +++ std::tie(begin, end) = std::equal_range(m_timer_queue.begin(), m_timer_queue.end(), t, timer_compare()); +++ if (begin == end) return; +++ begin = std::find(begin, end, t); +++ if (begin == end) return; +++ m_timer_queue.erase(begin); +++ } +++ +++ void simulation::rebind_socket(ip::tcp::socket* prev, ip::tcp::socket* s, ip::tcp::endpoint ep) +++ { +++ auto i = m_listen_sockets.find(ep); +++ assert(i != m_listen_sockets.end()); +++ if (i->second != prev) return; +++ i->second = s; +++ } +++ +++ ip::tcp::endpoint simulation::bind_socket(ip::tcp::socket* socket +++ , ip::tcp::endpoint ep, boost::system::error_code& ec) +++ { +++ assert(ep.address() != boost::asio::ip::address()); +++ +++ if (ep.port() < 1024 && ep.port() > 0) +++ { +++ // emulate process not running as root +++ ec = boost::asio::error::access_denied; +++ return ip::tcp::endpoint(); +++ } +++ +++ if (ep.port() == 0) +++ { +++ // if the socket is being bound to port 0, it means the system picks a +++ // free port. We want to avoid re-using ports, because that may confuse +++ // wireshark when threading together the TCP streams. +++ ep.port(m_next_bind_port++); +++ if (m_next_bind_port > 65534) m_next_bind_port = 2000; +++ +++ listen_socket_iter_t i = m_listen_sockets.lower_bound(ep); +++ while (i != m_listen_sockets.end() && i->first == ep) +++ { +++ ep.port(ep.port() + 1); +++ if (ep.port() > 65530) +++ { +++ ec = boost::asio::error::address_in_use; +++ return ip::tcp::endpoint(); +++ } +++ i = m_listen_sockets.lower_bound(ep); +++ } +++ } +++ +++ listen_socket_iter_t i = m_listen_sockets.lower_bound(ep); +++ if (i != m_listen_sockets.end() && i->first == ep) +++ { +++ ec = boost::asio::error::address_in_use; +++ return ip::tcp::endpoint(); +++ } +++ +++ m_listen_sockets.insert(i, std::make_pair(ep, socket)); +++ ec.clear(); +++ return ep; +++ } +++ +++ void simulation::unbind_socket(ip::tcp::socket* socket +++ , const ip::tcp::endpoint& ep) +++ { +++ listen_socket_iter_t i = m_listen_sockets.find(ep); +++ if (i == m_listen_sockets.end() || i->second != socket) return; +++ m_listen_sockets.erase(i); +++ } +++ +++ void simulation::rebind_udp_socket(ip::udp::socket* socket, ip::udp::endpoint ep) +++ { +++ auto i = m_udp_sockets.find(ep); +++ assert(i != m_udp_sockets.end()); +++ i->second = socket; +++ } +++ +++ ip::udp::endpoint simulation::bind_udp_socket(ip::udp::socket* socket +++ , ip::udp::endpoint ep, boost::system::error_code& ec) +++ { +++ assert(ep.address() != boost::asio::ip::address()); +++ +++ if (ep.port() < 1024 && ep.port() > 0) +++ { +++ // emulate process not running as root +++ ec = boost::asio::error::access_denied; +++ return ip::udp::endpoint(); +++ } +++ +++ if (ep.port() == 0) +++ { +++ // if the socket is being bound to port 0, it means the system picks a +++ // free port. +++ +++ ep.port(m_next_bind_port++); +++ if (m_next_bind_port > 65534) m_next_bind_port = 2000; +++ udp_socket_iter_t i = m_udp_sockets.lower_bound(ep); +++ while (i != m_udp_sockets.end() && i->first == ep) +++ { +++ ep.port(ep.port() + 1); +++ if (ep.port() > 65530) +++ { +++ ec = boost::asio::error::address_in_use; +++ return ip::udp::endpoint(); +++ } +++ i = m_udp_sockets.lower_bound(ep); +++ } +++ } +++ +++ udp_socket_iter_t i = m_udp_sockets.lower_bound(ep); +++ if (i != m_udp_sockets.end() && i->first == ep) +++ { +++ ec = boost::asio::error::address_in_use; +++ return ip::udp::endpoint(); +++ } +++ +++ m_udp_sockets.insert(i, std::make_pair(ep, socket)); +++ ec.clear(); +++ return ep; +++ } +++ +++ void simulation::unbind_udp_socket(ip::udp::socket* socket +++ , const ip::udp::endpoint& ep) +++ { +++ udp_socket_iter_t i = m_udp_sockets.find(ep); +++ if (i == m_udp_sockets.end() || i->second != socket) return; +++ m_udp_sockets.erase(i); +++ } +++ +++ std::shared_ptr simulation::internal_connect( +++ asio::ip::tcp::socket* s +++ , ip::tcp::endpoint const& target, boost::system::error_code& ec) +++ { +++ // find remote socket +++ listen_sockets_t::iterator i = m_listen_sockets.find(target); +++ if (i == m_listen_sockets.end()) +++ { +++ ec = boost::system::error_code(error::connection_refused); +++ return std::shared_ptr(); +++ } +++ +++ // make sure it's a listening socket +++ ip::tcp::socket* remote = i->second; +++ if (!remote->internal_is_listening()) +++ { +++ ec = boost::system::error_code(error::connection_refused); +++ return std::shared_ptr(); +++ } +++ +++ // create a channel +++ std::shared_ptr c = std::make_shared(); +++ +++ asio::ip::tcp::endpoint from = s->local_bound_to(ec); +++ +++ route network_route = m_config.channel_route(from.address() +++ , target.address()); +++ c->hops[0] = remote->get_outgoing_route() + network_route + s->get_incoming_route(); +++ c->hops[1] = s->get_outgoing_route() + network_route + remote->get_incoming_route(); +++ +++ c->ep[0] = s->local_bound_to(ec); +++ c->ep[1] = remote->local_bound_to(ec); +++ +++ c->visible_ep[0] = s->local_bound_to(ec); +++ c->visible_ep[1] = remote->local_bound_to(ec); +++ +++ aux::packet p; +++ p.type = aux::packet::type_t::syn; +++ p.overhead = 28; +++ p.from = asio::ip::udp::endpoint(from.address(), from.port()); +++ p.channel = c; +++ if (ec) return std::shared_ptr(); +++ +++ p.hops = c->hops[1]; +++ +++ forward_packet(std::move(p)); +++ +++ return c; +++ } +++ +++ route simulation::find_udp_socket(asio::ip::udp::socket const& socket +++ , ip::udp::endpoint const& ep) +++ { +++ udp_socket_iter_t i = m_udp_sockets.find(ep); +++ if (i == m_udp_sockets.end()) +++ return route(); +++ +++ ip::udp::endpoint src = socket.local_bound_to(); +++ route network_route = m_config.channel_route(src.address(), ep.address()); +++ +++ // ask the socket for its incoming route +++ network_route.append(i->second->get_incoming_route()); +++ +++ return network_route; +++ } +++ +++ void simulation::add_io_service(asio::io_context* ios) +++ { +++ bool added = m_nodes.insert(ios).second; +++ (void)added; +++ assert(added); +++ } +++ +++ void simulation::remove_io_service(asio::io_context* ios) +++ { +++ auto it = m_nodes.find(ios); +++ assert(it != m_nodes.end()); +++ m_nodes.erase(it); +++ } +++ +++ std::vector simulation::get_all_io_services() const +++ { +++ std::vector ret; +++ ret.reserve(m_nodes.size()); +++ std::remove_copy_if( +++ m_nodes.begin(), m_nodes.end(), std::back_inserter(ret) +++ , [](io_context* ios) { return ios->get_ips().empty(); }); +++ return ret; +++ } +++ +++ void simulation::log_pcap(char const* filename) +++ { +++ std::printf("saving packet capture to: \"%s\"\n", filename); +++ m_pcap = std::unique_ptr(new aux::pcap(filename)); +++ } +++ +++} +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/simulator.cpp ++@@ -0,0 +1,262 @@ +++/* +++ +++Copyright (c) 2015, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#include "simulator/simulator.hpp" +++#include "simulator/packet.hpp" +++ +++#include +++#include +++#include +++#include // for printf +++ +++#include "simulator/push_warnings.hpp" +++#include +++#include "simulator/pop_warnings.hpp" +++ +++typedef sim::chrono::high_resolution_clock::time_point time_point; +++typedef sim::chrono::high_resolution_clock::duration duration; +++ +++namespace sim { +++namespace asio { +++namespace ip { +++ +++ default_config default_cfg; +++ +++} // ip +++} // asio +++ +++void forward_packet(aux::packet p) +++{ +++ std::shared_ptr next_hop = p.hops.pop_front(); +++ if (!next_hop) +++ { +++ std::fprintf(stderr, "packet lost\n"); +++ return; +++ } +++ next_hop->incoming_packet(std::move(p)); +++} +++ +++namespace +++{ +++ // this is a dummy sink for endpoints, wrapping an io_context +++ struct endpoint : sink +++ { +++ endpoint(asio::io_context& ioc) +++ : m_ioc(ioc) +++ {} +++ +++ virtual void incoming_packet(aux::packet /* p */) override final { assert(false); } +++ +++ virtual std::string label() const override final +++ { +++ std::string ret; +++ for (auto const& ip : m_ioc.get_ips()) +++ { +++ ret += ip.to_string(); +++ ret += " "; +++ } +++ return ret; +++ } +++ +++ virtual std::string attributes() const override final +++ { +++ return "shape=ellipse"; +++ } +++ +++ private: +++ asio::io_context& m_ioc; +++ }; +++} +++ +++namespace +++{ +++ std::string escape_label(std::string n) +++ { +++ std::string ret; +++ for (auto c : n) +++ { +++ if (c == '\n') +++ { +++ ret += "\\n"; +++ continue; +++ } +++ if (c == '\"') +++ { +++ ret += "\\\""; +++ continue; +++ } +++ ret += c; +++ } +++ return ret; +++ } +++} +++ +++void dump_network_graph(simulation const& s, const std::string& filename) +++{ +++ // all edges (directed). +++ std::set, std::shared_ptr>> edges; +++ +++ // all network nodes +++ std::unordered_set> nodes; +++ +++ // local nodes (subgrapgs) +++ std::vector>> local_nodes; +++ +++ const std::vector io_services = s.get_all_io_services(); +++ +++ for (auto ioc : io_services) +++ { +++ std::shared_ptr ep = std::make_shared(*ioc); +++ local_nodes.push_back(std::unordered_set>()); +++ local_nodes.back().insert(ep); +++ +++ for (auto const& ip : ioc->get_ips()) +++ { +++ route in = ioc->get_incoming_route(ip); +++ route out = ioc->get_outgoing_route(ip); +++ +++ // this is the outgoing node for this endpoint. This is +++ // how it connects to the network. +++ const std::shared_ptr egress = out.empty() ? ep : out.last(); +++ +++ // first add both the incoming and outgoing chains +++ std::shared_ptr prev; +++ while (!in.empty()) +++ { +++ auto node = in.pop_front(); +++ local_nodes.back().insert(node); +++ if (prev) edges.insert({prev, node}); +++ prev = node; +++ } +++ if (prev) edges.insert({prev, ep}); +++ +++ prev = ep; +++ while (!out.empty()) +++ { +++ auto node = out.pop_front(); +++ local_nodes.back().insert(node); +++ edges.insert({prev, node}); +++ prev = node; +++ } +++ +++ // then connect the endpoint of those chains to the rest of the network. +++ // Since the network may be arbitrarily complex, we actually have to +++ // completely iterate over all other endpoints +++ +++ for (auto ios2 : io_services) +++ { +++ for (auto const& ip2 : ios2->get_ips()) +++ { +++ route network = s.config().channel_route( +++ ip, ip2); +++ +++ std::shared_ptr last = ios2->get_incoming_route(ip2).next_hop(); +++ +++ prev = egress; +++ while (!network.empty()) +++ { +++ auto node = network.pop_front(); +++ nodes.insert(node); +++ edges.insert({prev, node}); +++ prev = node; +++ } +++ edges.insert({prev, last}); +++ } +++ } +++ } +++ } +++ +++ // by now, the nodes and edges should represent the complete graph. Render it +++ // into dot. +++ +++ FILE* f = fopen(filename.c_str(), "w+"); +++ +++ std::fprintf(f, "digraph network {\n" +++ "concentrate=true;\n" +++ "overlap=scale;\n" +++ "splines=true;\n"); +++ +++ std::fprintf(f, "\n// nodes\n\n"); +++ +++ for (const auto& n : nodes) +++ { +++ std::string attributes = n->attributes(); +++ std::fprintf(f, " \"%p\" [label=\"%s\",style=\"filled\",color=\"red\"%s%s];\n" +++ , static_cast(n.get()) +++ , escape_label(n->label()).c_str() +++ , attributes.empty() ? "" : ", " +++ , attributes.c_str()); +++ } +++ +++ std::fprintf(f, "\n// local networks\n\n"); +++ +++ int idx = 0; +++ for (auto ln : local_nodes) +++ { +++ std::fprintf(f, "subgraph cluster_%d {\n", idx++); +++ +++ for (const auto& n : ln) +++ { +++ std::string attributes = n->attributes(); +++ std::fprintf(f, " \"%p\" [label=\"%s\",style=\"filled\",color=\"green\"%s%s];\n" +++ , static_cast(n.get()) +++ , escape_label(n->label()).c_str() +++ , attributes.empty() ? "" : ", " +++ , attributes.c_str()); +++ } +++ +++ std::fprintf(f, "}\n"); +++ } +++ +++ std::fprintf(f, "\n// edges\n\n"); +++ +++ while (!edges.empty()) +++ { +++ auto edge = *edges.begin(); +++ edges.erase(edges.begin()); +++ +++ std::fprintf(f, "\"%p\" -> \"%p\"\n" +++ , static_cast(edge.first.get()) +++ , static_cast(edge.second.get())); +++ } +++ +++ std::fprintf(f, "}\n"); +++ fclose(f); +++} +++ +++namespace aux { +++ +++ int channel::remote_idx(const asio::ip::tcp::endpoint& self) const +++ { +++ if (ep[0] == self) return 1; +++ if (ep[1] == self) return 0; +++ assert(false && "invalid socket"); +++ return -1; +++ } +++ +++ int channel::self_idx(const asio::ip::tcp::endpoint& self) const +++ { +++ if (ep[0] == self) return 0; +++ if (ep[1] == self) return 1; +++ assert(false && "invalid socket"); +++ return -1; +++ } +++ +++} // aux +++} // sim +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/sink_forwarder.cpp ++@@ -0,0 +1,45 @@ +++/* +++ +++Copyright (c) 2015, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#include "simulator/sink_forwarder.hpp" +++#include "simulator/packet.hpp" +++ +++namespace sim { namespace aux { +++ +++ sink_forwarder::sink_forwarder(sink* dst) +++ : m_dst(dst) +++ {} +++ +++ void sink_forwarder::incoming_packet(packet p) +++ { +++ if (m_dst == nullptr) return; +++ m_dst->incoming_packet(std::move(p)); +++ } +++ +++ std::string sink_forwarder::label() const +++ { +++ return m_dst ? m_dst->label() : ""; +++ } +++ +++ void sink_forwarder::reset(sink* s) +++ { +++ m_dst = s; +++ } +++ +++}} +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/socks_server.cpp ++@@ -0,0 +1,1033 @@ +++/* +++ +++Copyright (c) 2015, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#include "simulator/simulator.hpp" +++#include "simulator/socks_server.hpp" +++ +++#include +++#include +++#include +++#include // for printf +++ +++using namespace sim::asio; +++using namespace sim::asio::ip; +++ +++using boost::system::error_code; +++ +++namespace sim +++{ +++ using namespace aux; +++ +++ socks_server::socks_server(io_context& ios, unsigned short listen_port, int version +++ , std::uint32_t const flags) +++ : m_ios(ios) +++ , m_listen_socket(ios) +++ , m_conn(std::make_shared(m_ios, version, m_cmd_counts, flags, m_bind_port)) +++ , m_version(version) +++ , m_flags(flags) +++ { +++ m_cmd_counts.fill(0); +++ address local_ip = ios.get_ips().front(); +++ if (local_ip.is_v4()) +++ { +++ m_listen_socket.open(tcp::v4()); +++ m_listen_socket.bind(tcp::endpoint(address_v4::any(), listen_port)); +++ } +++ else +++ { +++ m_listen_socket.open(tcp::v6()); +++ m_listen_socket.bind(tcp::endpoint(address_v6::any(), listen_port)); +++ } +++ m_listen_socket.listen(); +++ +++ m_listen_socket.async_accept(m_conn->socket(), m_ep +++ , std::bind(&socks_server::on_accept, this, std::placeholders::_1)); +++ } +++ +++ void socks_server::on_accept(error_code const& ec) +++ { +++ if (ec == asio::error::operation_aborted) +++ return; +++ +++ if (ec) +++ { +++ std::printf("socks_server::on_accept: (%d) %s\n" +++ , ec.value(), ec.message().c_str()); +++ return; +++ } +++ +++ std::printf("socks_server accepted connection from: %s : %d\n", +++ m_ep.address().to_string().c_str(), m_ep.port()); +++ +++ m_conn->start(); +++ +++ // create a new connection to accept into +++ m_conn = std::make_shared(m_ios, m_version, m_cmd_counts, m_flags, m_bind_port); +++ +++ // now we can accept another connection +++ m_listen_socket.async_accept(m_conn->socket(), m_ep +++ , std::bind(&socks_server::on_accept, this, std::placeholders::_1)); +++ } +++ +++ void socks_server::stop() +++ { +++ m_close = true; +++ m_listen_socket.close(); +++ } +++ +++ socks_connection::socks_connection(asio::io_context& ios +++ , int version, std::array& cmd_counts, std::uint32_t const flags, int& bind_port) +++ : m_bind_port(bind_port) +++ , m_ios(ios) +++ , m_udp_resolver(ios) +++ , m_resolver(m_ios) +++ , m_client_connection(ios) +++ , m_server_connection(m_ios) +++ , m_bind_socket(m_ios) +++ , m_udp_associate(m_ios) +++ , m_num_out_bytes(0) +++ , m_num_in_bytes(0) +++ , m_version(version) +++ , m_command(0) +++ , m_cmd_counts(cmd_counts) +++ , m_flags(flags) +++ { +++ } +++ +++ void socks_connection::start() +++ { +++ if (m_version == 4) +++ { +++ asio::async_read(m_client_connection, asio::buffer(&m_out_buffer[0], 9) +++ , std::bind(&socks_connection::on_request1, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); +++ } else { +++ // read protocol version and number of auth-methods +++ asio::async_read(m_client_connection, asio::buffer(&m_out_buffer[0], 2) +++ , std::bind(&socks_connection::on_handshake1, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); +++ } +++ } +++ +++ void socks_connection::on_handshake1(error_code const& ec, size_t bytes_transferred) +++ { +++ if (ec || bytes_transferred != 2) +++ { +++ std::printf("socks_connection::on_handshake1: (%d) %s\n" +++ , ec.value(), ec.message().c_str()); +++ close_connection(); +++ return; +++ } +++ +++ if (m_out_buffer[0] != 4 && m_out_buffer[0] != 5) +++ { +++ std::printf("socks_connection::on_handshake1: unexpected socks protocol version: %d" +++ , int(m_out_buffer[0])); +++ close_connection(); +++ return; +++ } +++ +++ int num_methods = unsigned(m_out_buffer[1]); +++ +++ // read list of auth-methods +++ asio::async_read(m_client_connection, asio::buffer(&m_out_buffer[0], +++ num_methods) +++ , std::bind(&socks_connection::on_handshake2, shared_from_this() +++ , std::placeholders::_1, std::placeholders::_2)); +++ } +++ +++ void socks_connection::on_handshake2(error_code const& ec, size_t bytes_transferred) +++ { +++ if (ec) +++ { +++ std::printf("socks_connection::on_handshake2: (%d) %s\n" +++ , ec.value(), ec.message().c_str()); +++ close_connection(); +++ return; +++ } +++ +++ if (std::count(m_out_buffer, m_out_buffer + bytes_transferred, 0) == 0) +++ { +++ std::printf("socks_connection: could not find auth-method 0 (no-auth) in socks handshake\n"); +++ close_connection(); +++ return; +++ } +++ +++ m_in_buffer[0] = 5; // socks version +++ m_in_buffer[1] = 0; // auth-method (no-auth) +++ +++ asio::async_write(m_client_connection, asio::buffer(&m_in_buffer[0], 2) +++ , std::bind(&socks_connection::on_handshake3, shared_from_this() +++ , std::placeholders::_1, std::placeholders::_2)); +++ } +++ +++ void socks_connection::on_handshake3(error_code const& ec, size_t bytes_transferred) +++ { +++ if (ec || bytes_transferred != 2) +++ { +++ std::printf("socks_connection::on_handshake3: (%d) %s\n" +++ , ec.value(), ec.message().c_str()); +++ close_connection(); +++ return; +++ } +++ +++ asio::async_read(m_client_connection, asio::buffer(&m_out_buffer[0], 10) +++ , std::bind(&socks_connection::on_request1, shared_from_this() +++ , std::placeholders::_1, std::placeholders::_2)); +++ } +++ +++ void socks_connection::on_request1(error_code const& ec, size_t bytes_transferred) +++ { +++ size_t const expected = m_version == 4 ? 9 : 10; +++ if (ec || bytes_transferred != expected) +++ { +++ std::printf("socks_connection::on_request1: (%d) %s\n" +++ , ec.value(), ec.message().c_str()); +++ close_connection(); +++ return; +++ } +++ +++// +----+-----+-------+------+----------+----------+ +++// |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | +++// +----+-----+-------+------+----------+----------+ +++// | 1 | 1 | X'00' | 1 | Variable | 2 | +++// +----+-----+-------+------+----------+----------+ +++ +++ int const version = m_out_buffer[0]; +++ int const command = m_out_buffer[1]; +++ m_command = command; +++ ++m_cmd_counts[command - 1]; +++ +++ if (version != m_version) +++ { +++ std::printf("socks_connection::on_request1: unexpected socks protocol version: %d expected: %d\n" +++ , int(m_out_buffer[0]), m_version); +++ close_connection(); +++ return; +++ } +++ +++ if (m_version == 4) +++ { +++ if (command != 1 && command != 2) +++ { +++ std::printf("socks_connection::on_request1: unexpected socks command: %d\n" +++ , command); +++ close_connection(); +++ return; +++ } +++ +++ std::uint16_t port = m_out_buffer[2] & 0xff; +++ port <<= 8; +++ port |= m_out_buffer[3] & 0xff; +++ +++ std::uint32_t addr = m_out_buffer[4] & 0xff; +++ addr <<= 8; +++ addr |= m_out_buffer[5] & 0xff; +++ addr <<= 8; +++ addr |= m_out_buffer[6] & 0xff; +++ addr <<= 8; +++ addr |= m_out_buffer[7] & 0xff; +++ +++ if (m_out_buffer[8] != 0) +++ { +++ // in this case, we would have to read one byte at a time until we +++ // get to the null terminator. +++ std::printf("socks_connection::on_request1: username in SOCKS4 mode not supported\n"); +++ close_connection(); +++ return; +++ } +++ asio::ip::tcp::endpoint target(asio::ip::address_v4(addr), port); +++ if (command == 1) +++ { +++ open_forward_connection(target); +++ } +++ else if (command == 2) +++ { +++ bind_connection(target); +++ } +++ +++ return; +++ } +++ +++ if (command != 1 && command != 2 && command != 3) +++ { +++ std::printf("socks_connection::on_request1: unexpected command: %d\n" +++ , command); +++ close_connection(); +++ return; +++ } +++ +++ if (m_out_buffer[2] != 0) +++ { +++ std::printf("socks_connection::on_request1: reserved byte is non-zero: %d\n" +++ , int(m_out_buffer[2])); +++ close_connection(); +++ return; +++ } +++ +++ int atyp = unsigned(m_out_buffer[3]); +++ +++ if (atyp != 1 && atyp != 3 && atyp != 4) +++ { +++ std::printf("socks_connection::on_request1: unexpected address type in SOCKS request: %d\n" +++ , atyp); +++ close_connection(); +++ return; +++ } +++ +++ std::printf("socks_connection: received %s request address type: %d\n" +++ , command == 1 ? "CONNECT" +++ : command == 2 ? "BIND" +++ : "UDP_ASSOCIATE", atyp); +++ +++ switch (atyp) +++ { +++ case 1: { // IPv4 address (we have the whole request already) +++ +++// +----+-----+-------+------+----------+----------+ +++// |VER | CMD | RSV | ATYP | BND.ADDR | BND.PORT | +++// +----+-----+-------+------+----------+----------+ +++// | 1 | 1 | X'00' | 1 | 4 | 2 | +++// +----+-----+-------+------+----------+----------+ +++ +++ std::uint32_t addr = m_out_buffer[4] & 0xff; +++ addr <<= 8; +++ addr |= m_out_buffer[5] & 0xff; +++ addr <<= 8; +++ addr |= m_out_buffer[6] & 0xff; +++ addr <<= 8; +++ addr |= m_out_buffer[7] & 0xff; +++ +++ std::uint16_t port = m_out_buffer[8] & 0xff; +++ port <<= 8; +++ port |= m_out_buffer[9] & 0xff; +++ +++ asio::ip::tcp::endpoint target(asio::ip::address_v4(addr), port); +++ if (command == 1) +++ { +++ open_forward_connection(target); +++ } +++ else if (command == 2) +++ { +++ bind_connection(target); +++ } +++ else if (command == 3) +++ { +++ if (target.address() == address()) +++ { +++ target.address(m_client_connection.remote_endpoint().address()); +++ } +++ udp_associate(target); +++ } +++ +++ break; +++ } +++ case 3: { // domain name +++ +++// +----+-----+-------+------+-----+----------+----------+ +++// |VER | CMD | RSV | ATYP | LEN | BND.ADDR | BND.PORT | +++// +----+-----+-------+------+-----+----------+----------+ +++// | 1 | 1 | X'00' | 1 | 1 | Variable | 2 | +++// +----+-----+-------+------+-----+----------+----------+ +++ +++ if (command == 2) +++ { +++ std::printf("ERROR: cannot BIND to hostname address (only IPv4 or IPv6 addresses)\n"); +++ close_connection(); +++ return; +++ } +++ +++ const int len = std::uint8_t(m_out_buffer[4]); +++ // we already read an address of length 4, assuming it was an IPv4 +++ // address. Now, with a domain name, one of those bytes was the +++ // length-prefix, but we still read 3 bytes already. +++ const int additional_bytes = len - 3; +++ asio::async_read(m_client_connection, asio::buffer(&m_out_buffer[10], additional_bytes) +++ , std::bind(&socks_connection::on_request_domain_name +++ , shared_from_this(), std::placeholders::_1, std::placeholders::_2)); +++ break; +++ } +++ case 4: // IPv6 address +++ +++// +----+-----+-------+------+----------+----------+ +++// |VER | CMD | RSV | ATYP | BND.ADDR | BND.PORT | +++// +----+-----+-------+------+----------+----------+ +++// | 1 | 1 | X'00' | 1 | 16 | 2 | +++// +----+-----+-------+------+----------+----------+ +++ +++ std::printf("ERROR: unsupported address type %d\n", atyp); +++ close_connection(); +++ } +++ } +++ +++ void socks_connection::on_request_domain_name(error_code const& ec, size_t bytes_transferred) +++ { +++ if (ec) +++ { +++ std::printf("socks_connection::on_request_domain_name(%s): (%d) %s\n" +++ , command(), ec.value(), ec.message().c_str()); +++ close_connection(); +++ return; +++ } +++ +++ int const buffer_size = int(10 + bytes_transferred); +++ +++ std::uint16_t port = m_out_buffer[buffer_size - 2] & 0xff; +++ port <<= 8; +++ port |= m_out_buffer[buffer_size - 1] & 0xff; +++ +++ std::string hostname(&m_out_buffer[5], std::uint8_t(m_out_buffer[4])); +++ std::printf("socks_connection::on_request_domain_name(%s): hostname: %s port: %d\n" +++ , command(), hostname.c_str(), port); +++ +++ char port_str[10]; +++ std::snprintf(port_str, sizeof(port_str), "%d", port); +++ m_resolver.async_resolve(hostname, port_str +++ , std::bind(&socks_connection::on_request_domain_lookup +++ , shared_from_this(), std::placeholders::_1, std::placeholders::_2)); +++ } +++ +++ void socks_connection::on_request_domain_lookup(boost::system::error_code const& ec +++ , asio::ip::tcp::resolver::results_type const ips) +++ { +++ if (ec || ips.empty()) +++ { +++ if (ec) +++ { +++ std::printf("socks_connection::on_request_domain_lookup(%s): (%d) %s\n" +++ , command(), ec.value(), ec.message().c_str()); +++ } +++ else +++ { +++ std::printf("socks_connection::on_request_domain_lookup(%s): empty response\n" +++ , command()); +++ } +++ +++// +----+-----+-------+------+----------+----------+ +++// |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | +++// +----+-----+-------+------+----------+----------+ +++// | 1 | 1 | X'00' | 1 | Variable | 2 | +++// +----+-----+-------+------+----------+----------+ +++ +++ m_in_buffer[0] = char(m_version); // version +++ m_in_buffer[1] = 4; // response (host unreachable) +++ m_in_buffer[2] = 0; // reserved +++ m_in_buffer[3] = 1; // IPv4 +++ memset(&m_in_buffer[4], 0, 4); +++ m_in_buffer[8] = 0; // port +++ m_in_buffer[9] = 0; +++ +++ auto self = shared_from_this(); +++ asio::async_write(m_client_connection +++ , asio::buffer(&m_in_buffer[0], 10) +++ , [=](boost::system::error_code const&, size_t) +++ { +++ self->close_connection(); +++ }); +++ return; +++ } +++ +++ std::printf("socks_connection::on_request_domain_lookup(%s): connecting to: %s port: %d\n" +++ , command() +++ , ips.front().endpoint().address().to_string().c_str() +++ , ips.front().endpoint().port()); +++ open_forward_connection(ips.front().endpoint()); +++ } +++ +++ void socks_connection::open_forward_connection(const asio::ip::tcp::endpoint& target) +++ { +++ std::printf("socks_connection::open_forward_connection(%s): connecting to %s port %d\n" +++ , command(), target.address().to_string().c_str(), target.port()); +++ +++ m_server_connection.open(target.protocol()); +++ m_server_connection.async_connect(target +++ , std::bind(&socks_connection::on_connected, shared_from_this() +++ , std::placeholders::_1)); +++ } +++ +++ void socks_connection::bind_connection(const asio::ip::tcp::endpoint& target) +++ { +++ std::printf("socks_connection::bind_connection(%s): binding to %s port %d\n" +++ , command(), target.address().to_string().c_str(), target.port()); +++ +++ error_code ec; +++ m_bind_socket.open(target.protocol(), ec); +++ if (ec) +++ { +++ std::printf("ERROR: open bind socket failed: (%d) %s\n", ec.value() +++ , ec.message().c_str()); +++ } +++ else +++ { +++ m_bind_socket.bind(target, ec); +++ } +++ +++ int const response = ec +++ ? (m_version == 4 ? 91 : 1) +++ : (m_version == 4 ? 90 : 0); +++ tcp::endpoint ep = m_bind_socket.local_endpoint(); +++ int const len = format_response(ep.address(), ep.port(), response); +++ +++ if (ec) +++ { +++ std::printf("ERROR: binding socket to %s %d failed: (%d) %s\n" +++ , target.address().to_string().c_str() +++ , target.port() +++ , ec.value() +++ , ec.message().c_str()); +++ +++ auto self = shared_from_this(); +++ +++ asio::async_write(m_client_connection +++ , asio::buffer(&m_in_buffer[0], len) +++ , [=](boost::system::error_code const&, size_t) +++ { +++ self->close_connection(); +++ }); +++ return; +++ } +++ +++ // send response +++ asio::async_write(m_client_connection +++ , asio::buffer(&m_in_buffer[0], len) +++ , std::bind(&socks_connection::start_accept, shared_from_this(), std::placeholders::_1)); +++ } +++ +++ void socks_connection::udp_associate(const asio::ip::tcp::endpoint& target) +++ { +++ std::printf("socks_connection::udp_associate(%s): %s:%d\n" +++ , command(), target.address().to_string().c_str(), target.port()); +++ +++ m_udp_associate_ep.address(target.address()); +++ m_udp_associate_ep.port(target.port()); +++ +++ error_code ec; +++ m_udp_associate.open(m_udp_associate_ep.protocol(), ec); +++ if (ec) +++ { +++ std::printf("ERROR: open UDP associate socket failed: (%d) %s\n", ec.value() +++ , ec.message().c_str()); +++ } +++ else +++ { +++ m_udp_associate.bind(udp::endpoint(address_v4(), std::uint16_t(m_bind_port++)), ec); +++ if (ec) +++ { +++ std::printf("ERROR: binding socket failed: (%d) %s\n" +++ , ec.value(), ec.message().c_str()); +++ } +++ else +++ { +++ m_udp_associate.non_blocking(true); +++ m_udp_associate.async_receive_from(boost::asio::buffer(m_udp_buffer) +++ , m_udp_from, 0, std::bind(&socks_connection::on_read_udp, this, std::placeholders::_1, std::placeholders::_2)); +++ } +++ } +++ +++ int const response = ec ? 1 : 0; +++ udp::endpoint ep = m_udp_associate.local_bound_to(); +++ int const len = (m_flags & udp_associate_respond_empty_hostname) +++ ? format_hostname_response("foobar", ep.port(), response) +++ : format_response(ep.address(), ep.port(), response); +++ +++ if (ec) +++ { +++ auto self = shared_from_this(); +++ +++ asio::async_write(m_client_connection +++ , asio::buffer(&m_in_buffer[0], len) +++ , [=](boost::system::error_code const&, size_t) +++ { +++ self->close_connection(); +++ }); +++ return; +++ } +++ +++ // send response +++ asio::async_write(m_client_connection, asio::buffer(&m_in_buffer[0], len) +++ , std::bind(&socks_connection::wait_for_eof, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); +++ } +++ +++ void socks_connection::wait_for_eof(boost::system::error_code const& ec, std::size_t) +++ { +++ if (ec) +++ { +++ std::printf("socks_connection::wait_for_eof: %s\n", ec.message().c_str()); +++ m_udp_associate.close(); +++ m_udp_associate_ep = udp::endpoint(); +++ m_client_connection.close(); +++ return; +++ } +++ +++ if (m_flags & socks_flag::disconnect_udp_associate) +++ { +++ std::printf("socks_connection::wait_for_eof: closing connection prematurely\n"); +++ m_client_connection.close(); +++ return; +++ } +++ +++ m_client_connection.async_read_some( +++ asio::buffer(m_out_buffer) +++ , std::bind(&socks_connection::wait_for_eof, shared_from_this() +++ , std::placeholders::_1, std::placeholders::_2)); +++ } +++ +++ void socks_connection::on_read_udp(boost::system::error_code const& ec +++ , std::size_t bytes_transferred) +++ { +++ std::printf("socks_connection::on_read_udp\n"); +++ if (ec) +++ { +++ std::printf("socks_connection::on_read_udp: %s\n", ec.message().c_str()); +++ return; +++ } +++ +++ // if the client didn't specify an IP and port it would send packets from, +++ // we assumed the same IP as the TCP connection and assume the port is the +++ // same as the first UDP packet from that host +++ if (m_udp_associate_ep.port() == 0 +++ && m_udp_from.address() == m_udp_associate_ep.address()) +++ { +++ m_udp_associate_ep.port(m_udp_from.port()); +++ } +++ +++ if (m_udp_from == m_udp_associate_ep) +++ { +++ // read UDP ASSICOATE header and forward outgoing packet +++ // +----+------+------+----------+----------+----------+ +++ // |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA | +++ // +----+------+------+----------+----------+----------+ +++ // | 2 | 1 | 1 | Variable | 2 | Variable | +++ // +----+------+------+----------+----------+----------+ +++ +++ char const* buf = m_udp_buffer.data(); +++ if (buf[2] != 0) std::printf("fragment != 0, not supported\n"); +++ +++ int const atyp = buf[3]; +++ if (atyp == 3) +++ { +++ // hostname +++ int const len = buf[4]; +++ +++ buf += 5; +++ bytes_transferred -= 5; +++ std::string const hostname(buf, len); +++ buf += len; +++ bytes_transferred -= len; +++ +++ std::uint16_t port = buf[0] & 0xff; +++ port <<= 8; +++ port |= buf[1] & 0xff; +++ buf += 2; +++ bytes_transferred -= 2; +++ +++ auto it = m_name_mapping.right.find(hostname); +++ if (it != m_name_mapping.right.end()) +++ { +++ error_code err; +++ m_udp_associate.send_to(boost::asio::buffer(buf, bytes_transferred) +++ , udp::endpoint(it->second, port), 0, err); +++ if (err) std::printf("send_to failed: %s\n", err.message().c_str()); +++ return; +++ } +++ +++ std::vector forward_buffer(buf, buf + bytes_transferred); +++ +++ m_udp_resolver.async_resolve(hostname.c_str(), std::to_string(port).c_str() +++ , [buf=std::move(forward_buffer), hostname, this] +++ (error_code const& ec, asio::ip::udp::resolver::results_type ips) +++ { +++ if (ec) +++ { +++ std::printf("resolve failed: %s\n", ec.message().c_str()); +++ return; +++ } +++ +++ for (auto const& ip : ips) +++ { +++ auto const target = ip.endpoint(); +++ error_code err; +++ m_udp_associate.send_to(boost::asio::buffer(buf) +++ , target, 0, err); +++ if (!err) +++ { +++ m_name_mapping.insert({target.address(), hostname}); +++ break; +++ } +++ std::printf("send_to failed: %s\n", err.message().c_str()); +++ } +++ }); +++ } +++ else if (atyp == 1) +++ { +++ // IPv4 +++ std::uint32_t addr = buf[4] & 0xff; +++ addr <<= 8; +++ addr |= buf[5] & 0xff; +++ addr <<= 8; +++ addr |= buf[6] & 0xff; +++ addr <<= 8; +++ addr |= buf[7] & 0xff; +++ +++ std::uint16_t port = buf[8] & 0xff; +++ port <<= 8; +++ port |= buf[9] & 0xff; +++ +++ buf += 10; +++ bytes_transferred -= 10; +++ +++ asio::ip::udp::endpoint const target(address_v4(addr), port); +++ +++ error_code err; +++ m_udp_associate.send_to(boost::asio::buffer(buf, bytes_transferred), target, 0, err); +++ if (err) std::printf("send_to failed: %s\n", err.message().c_str()); +++ } +++ else +++ { +++ std::printf("only supports IPv4 and hostname. ATYP: %d\n", atyp); +++ } +++ } +++ else +++ { +++ std::uint16_t const from_port = m_udp_from.port(); +++ +++ auto it = m_name_mapping.left.find(m_udp_from.address()); +++ if (it != m_name_mapping.left.end()) +++ { +++ std::vector header(7 + it->second.size()); +++ header[0] = 0; // RSV +++ header[1] = 0; +++ header[2] = 0; // fragment +++ header[3] = 3; // ATYP +++ header[4] = static_cast(it->second.size()); +++ int idx = 5; +++ std::copy(it->second.begin(), it->second.end(), header.data() + idx); +++ idx += static_cast(it->second.size()); +++ header[idx] = (from_port >> 8) & 0xff; +++ header[idx + 1] = from_port & 0xff; +++ +++ std::array vec{{ +++ {header.data(), header.size()}, +++ {m_udp_buffer.data(), bytes_transferred}}}; +++ +++ error_code err; +++ m_udp_associate.send_to(vec, m_udp_associate_ep, 0, err); +++ if (err) std::printf("send_to failed: %s\n", err.message().c_str()); +++ } +++ else +++ { +++ // add UDP ASSOCIATE header and forward to client +++ std::uint32_t const from_addr = m_udp_from.address().to_v4().to_uint(); +++ std::array header; +++ header[0] = 0; // RSV +++ header[1] = 0; +++ header[2] = 0; // fragment +++ header[3] = 1; // ATYP +++ header[4] = (from_addr >> 24) & 0xff; // Address +++ header[5] = (from_addr >> 16) & 0xff; +++ header[6] = (from_addr >> 8) & 0xff; +++ header[7] = (from_addr) & 0xff; +++ header[8] = (from_port >> 8) & 0xff; +++ header[9] = from_port & 0xff; +++ +++ std::array vec{{ +++ {header.data(), header.size()}, +++ {m_udp_buffer.data(), bytes_transferred}}}; +++ +++ error_code err; +++ m_udp_associate.send_to(vec, m_udp_associate_ep, 0, err); +++ if (err) std::printf("send_to failed: %s\n", err.message().c_str()); +++ } +++ } +++ +++ m_udp_associate.async_receive_from(boost::asio::buffer(m_udp_buffer) +++ , m_udp_from, 0, std::bind(&socks_connection::on_read_udp, this, std::placeholders::_1, std::placeholders::_2)); +++ } +++ +++ void socks_connection::start_accept(boost::system::error_code const& ec) +++ { +++ if (ec) +++ { +++ std::printf("socks_connection(%s): error writing to client: (%d) %s\n" +++ , command(), ec.value(), ec.message().c_str()); +++ close_connection(); +++ return; +++ } +++ +++ m_bind_socket.listen(); +++ m_bind_socket.async_accept(m_server_connection +++ , std::bind(&socks_connection::on_connected +++ , shared_from_this(), std::placeholders::_1)); +++ +++ m_client_connection.async_read_some( +++ sim::asio::buffer(m_out_buffer) +++ , std::bind(&socks_connection::on_client_receive, shared_from_this() +++ , std::placeholders::_1, std::placeholders::_2)); +++ } +++ +++ int socks_connection::format_response(address const& addr, int const port +++ , int const response) +++ { +++ int i = 0; +++ if (m_version == 5) +++ { +++// +----+-----+-------+------+----------+----------+ +++// |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | +++// +----+-----+-------+------+----------+----------+ +++// | 1 | 1 | X'00' | 1 | Variable | 2 | +++// +----+-----+-------+------+----------+----------+ +++ +++ m_in_buffer[i++] = char(m_version); // version +++ m_in_buffer[i++] = char(response); // response +++ m_in_buffer[i++] = 0; // reserved +++ if (addr.is_v4()) +++ { +++ m_in_buffer[i++] = 1; // IPv4 +++ address_v4::bytes_type b = addr.to_v4().to_bytes(); +++ memcpy(&m_in_buffer[i], &b[0], b.size()); +++ i += int(b.size()); +++ } else { +++ m_in_buffer[i++] = 4; // IPv6 +++ address_v6::bytes_type b = addr.to_v6().to_bytes(); +++ memcpy(&m_in_buffer[i], &b[0], b.size()); +++ i += int(b.size()); +++ } +++ +++ m_in_buffer[i++] = (port >> 8) & 0xff; +++ m_in_buffer[i++] = port & 0xff; +++ } +++ else +++ { +++ m_in_buffer[i++] = 0; // response version +++ m_in_buffer[i++] = char(response); // return code +++ +++ assert(addr.is_v4()); +++ +++ m_in_buffer[i++] = (port >> 8) & 0xff; +++ m_in_buffer[i++] = port & 0xff; +++ +++ address_v4::bytes_type b = addr.to_v4().to_bytes(); +++ memcpy(&m_in_buffer[i], &b[0], b.size()); +++ i += int(b.size()); +++ } +++ return i; +++ } +++ +++ int socks_connection::format_hostname_response(char const* hostname, int const port +++ , int const response) +++ { +++ int i = 0; +++ if (m_version != 5) +++ { +++ std::printf("socks_connection: hostname response requires SOCKS v5\n"); +++ close_connection(); +++ return 0; +++ } +++// +----+-----+-------+------+----------+----------+ +++// |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | +++// +----+-----+-------+------+----------+----------+ +++// | 1 | 1 | X'00' | 1 | Variable | 2 | +++// +----+-----+-------+------+----------+----------+ +++ +++ m_in_buffer[i++] = char(m_version); // version +++ m_in_buffer[i++] = char(response); // response +++ m_in_buffer[i++] = 0; // reserved +++ m_in_buffer[i++] = 3; // DOMAINNAME +++ m_in_buffer[i++] = std::uint8_t(::strlen(hostname)); +++ for (; *hostname != '\0'; ++hostname) +++ m_in_buffer[i++] = *hostname; +++ +++ m_in_buffer[i++] = (port >> 8) & 0xff; +++ m_in_buffer[i++] = port & 0xff; +++ return i; +++ } +++ +++ void socks_connection::on_connected(boost::system::error_code const& ec) +++ { +++ std::printf("socks_connection(%s): on_connect: (%d) %s\n" +++ , command(), ec.value(), ec.message().c_str()); +++ +++ if (ec == asio::error::operation_aborted +++ || ec == boost::system::errc::bad_file_descriptor) +++ { +++ return; +++ } +++ +++ boost::system::error_code err; +++ asio::ip::tcp::endpoint const ep = m_server_connection.remote_endpoint(err); +++ if (!err) +++ { +++ std::printf("socks_connection(%s): remote_endpoint: %s %d\n" +++ , command(), ep.address().to_string().c_str(), ep.port()); +++ } +++ +++ int const response = ec +++ ? (m_version == 4 ? 91 : 5) +++ : (m_version == 4 ? 90 : 0); +++ int const len = format_response(ep.address(), ep.port(), response); +++ +++ if (ec) +++ { +++ std::printf("socks_connection(%s): failed to connect to/accept from target server: (%d) %s\n" +++ , command(), ec.value(), ec.message().c_str()); +++ +++ auto self = shared_from_this(); +++ +++ asio::async_write(m_client_connection +++ , asio::buffer(&m_in_buffer[0], len) +++ , [=](boost::system::error_code const&, size_t) +++ { +++ self->close_connection(); +++ }); +++ return; +++ } +++ +++ auto self = shared_from_this(); +++ +++ asio::async_write(m_client_connection +++ , asio::buffer(&m_in_buffer[0], len) +++ , [this, self](boost::system::error_code const& ec, size_t) +++ { +++ if (ec) +++ { +++ std::printf("socks_connection(%s): error writing to client: (%d) %s\n" +++ , command(), ec.value(), ec.message().c_str()); +++ return; +++ } +++ +++ // read from the client and from the server +++ self->m_server_connection.async_read_some( +++ sim::asio::buffer(m_in_buffer) +++ , std::bind(&socks_connection::on_server_receive, self +++ , std::placeholders::_1, std::placeholders::_2)); +++ self->m_client_connection.async_read_some( +++ sim::asio::buffer(m_out_buffer) +++ , std::bind(&socks_connection::on_client_receive, self +++ , std::placeholders::_1, std::placeholders::_2)); +++ }); +++ } +++ +++ // we received some data from the client, forward it to the server +++ void socks_connection::on_client_receive(boost::system::error_code const& ec +++ , std::size_t bytes_transferred) +++ { +++ // bad file descriptor means the socket has been closed. Whoever closed +++ // the socket will have opened a new one, we cannot call +++ // close_connection() +++ if (ec == asio::error::operation_aborted +++ || ec == boost::system::errc::bad_file_descriptor) +++ return; +++ +++ if (ec) +++ { +++ std::printf("socks_connection (%s): error reading from client: (%d) %s\n" +++ , command(), ec.value(), ec.message().c_str()); +++ close_connection(); +++ return; +++ } +++ asio::async_write(m_server_connection, asio::buffer(&m_out_buffer[0], bytes_transferred) +++ , std::bind(&socks_connection::on_client_forward, shared_from_this() +++ , std::placeholders::_1, std::placeholders::_2)); +++ } +++ +++ void socks_connection::on_client_forward(error_code const& ec +++ , size_t /* bytes_transferred */) +++ { +++ if (ec) +++ { +++ std::printf("socks_connection(%s): error writing to server: (%d) %s\n" +++ , command(), ec.value(), ec.message().c_str()); +++ close_connection(); +++ return; +++ } +++ +++ m_client_connection.async_read_some( +++ sim::asio::buffer(m_out_buffer) +++ , std::bind(&socks_connection::on_client_receive, shared_from_this() +++ , std::placeholders::_1, std::placeholders::_2)); +++ } +++ +++ // we received some data from the server, forward it to the server +++ void socks_connection::on_server_receive(boost::system::error_code const& ec +++ , std::size_t bytes_transferred) +++ { +++ if (ec) +++ { +++ std::printf("socks_connection(%s): error reading from server: (%d) %s\n" +++ , command(), ec.value(), ec.message().c_str()); +++ close_connection(); +++ return; +++ } +++ +++ asio::async_write(m_client_connection, asio::buffer(&m_in_buffer[0], bytes_transferred) +++ , std::bind(&socks_connection::on_server_forward, shared_from_this() +++ , std::placeholders::_1, std::placeholders::_2)); +++ } +++ +++ void socks_connection::on_server_forward(error_code const& ec +++ , size_t /* bytes_transferred */) +++ { +++ if (ec) +++ { +++ std::printf("socks_connection(%s): error writing to client: (%d) %s\n" +++ , command(), ec.value(), ec.message().c_str()); +++ close_connection(); +++ return; +++ } +++ +++ m_server_connection.async_read_some( +++ sim::asio::buffer(m_in_buffer) +++ , std::bind(&socks_connection::on_server_receive, shared_from_this() +++ , std::placeholders::_1, std::placeholders::_2)); +++ } +++ +++ void socks_connection::close_connection() +++ { +++ error_code err; +++ m_client_connection.close(err); +++ if (err) +++ { +++ std::printf("socks_connection::close: failed to close client connection (%d) %s\n" +++ , err.value(), err.message().c_str()); +++ } +++ m_server_connection.close(err); +++ if (err) +++ { +++ std::printf("socks_connection::close: failed to close server connection (%d) %s\n" +++ , err.value(), err.message().c_str()); +++ } +++ +++ m_bind_socket.close(err); +++ if (err) +++ { +++ std::printf("socks_connection::close: failed to close bind socket (%d) %s\n" +++ , err.value(), err.message().c_str()); +++ } +++ } +++ +++ char const* socks_connection::command() const +++ { +++ switch (m_command) +++ { +++ case 1: return "CONNECT"; +++ case 2: return "BIND"; +++ case 3: return "UDP_ASSOCIATE"; +++ default: return "UNKNOWN"; +++ } +++ } +++} +++ +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/tcp_socket.cpp ++@@ -0,0 +1,929 @@ +++/* +++ +++Copyright (c) 2015, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#include "simulator/simulator.hpp" +++#include "simulator/packet.hpp" +++#include "simulator/pcap.hpp" +++#include "simulator/handler_allocator.hpp" +++ +++#include +++#include +++#include // for printf +++ +++#include "simulator/push_warnings.hpp" +++#include +++#include +++#include "simulator/pop_warnings.hpp" +++ +++typedef sim::chrono::high_resolution_clock::time_point time_point; +++typedef sim::chrono::high_resolution_clock::duration duration; +++ +++using namespace std::placeholders; +++ +++namespace sim { +++namespace asio { +++namespace ip { +++ +++ tcp::socket::socket(io_context& ios) +++ : socket_base(ios) +++ , m_connect_timer(ios) +++ , m_recv_timer(ios) +++ {} +++ +++ tcp::socket::socket(socket&& s) +++ : socket_base(std::move(s)) +++ , m_connect_handler(std::move(s.m_connect_handler)) +++ , m_connect_timer(std::move(s.m_connect_timer)) +++ , m_mss(s.m_mss) +++ , m_send_handler(std::move(s.m_send_handler)) +++ , m_wait_send_handler(std::move(s.m_wait_send_handler)) +++ , m_send_buffer(std::move(s.m_send_buffer)) +++ , m_incoming_queue(std::move(s.m_incoming_queue)) +++ , m_queue_size(std::move(s.m_queue_size)) +++ , m_recv_handler(std::move(s.m_recv_handler)) +++ , m_wait_recv_handler(std::move(s.m_wait_recv_handler)) +++ , m_recv_buffer(std::move(s.m_recv_buffer)) +++ , m_recv_timer(std::move(s.m_recv_timer)) +++ , m_is_v4(std::move(s.m_is_v4)) +++ , m_recv_null_buffers(std::move(s.m_recv_null_buffers)) +++ , m_send_null_buffers(std::move(s.m_send_null_buffers)) +++ , m_channel(std::move(s.m_channel)) +++ , m_next_outgoing_seq(std::move(s.m_next_outgoing_seq)) +++ , m_next_incoming_seq(std::move(s.m_next_incoming_seq)) +++ , m_last_drop_seq(std::move(s.m_last_drop_seq)) +++ , m_cwnd(std::move(s.m_cwnd)) +++ , m_bytes_in_flight(std::move(s.m_bytes_in_flight)) +++ , m_reorder_buffer(std::move(s.m_reorder_buffer)) +++ , m_outstanding_packet_sizes(std::move(s.m_outstanding_packet_sizes)) +++ , m_outgoing_packets(std::move(s.m_outgoing_packets)) +++ { +++ if (m_forwarder) m_forwarder->reset(this); +++ s.m_forwarder.reset(); +++ s.m_open = false; +++ s.m_bound_to = ip::tcp::endpoint(); +++ +++ if (m_bound_to != ip::tcp::endpoint()) +++ m_io_service.rebind_socket(&s, this, m_bound_to); +++ } +++ +++ tcp::socket::~socket() +++ { +++ boost::system::error_code ec; +++ +++ m_channel.reset(); +++ +++ if (m_bound_to != ip::tcp::endpoint()) +++ { +++ m_io_service.unbind_socket(this, m_bound_to); +++ m_bound_to = ip::tcp::endpoint(); +++ m_user_bound_to = ip::tcp::endpoint(); +++ } +++ m_open = false; +++ +++ // prevent any more packets from being delivered to this socket +++ if (m_forwarder) +++ { +++ m_forwarder->reset(); +++ m_forwarder.reset(); +++ } +++ cancel(ec); +++ } +++ +++ void tcp::socket::open(tcp protocol, boost::system::error_code& ec) try +++ { +++ close(ec); +++ m_open = true; +++ m_is_v4 = (protocol == ip::tcp::v4()); +++ ec.clear(); +++ m_forwarder = std::make_shared(this); +++ } +++ catch (std::bad_alloc const&) +++ { +++ ec = make_error_code(boost::system::errc::not_enough_memory); +++ } +++ catch (boost::system::system_error const& err) +++ { +++ ec = err.code(); +++ } +++ +++ void tcp::socket::open(tcp protocol) +++ { +++ boost::system::error_code ec; +++ open(protocol, ec); +++ if (ec) throw boost::system::system_error(ec); +++ } +++ +++ // used to attach an incoming connection to this +++ void tcp::socket::internal_connect(tcp::endpoint const& bind_ip +++ , std::shared_ptr const& c +++ , boost::system::error_code& ec) +++ { +++ open(m_is_v4 ? tcp::v4() : tcp::v6(), ec); +++ if (ec) +++ { +++ std::printf("tcp::socket::internal_connect() error: (%d) %s\n" +++ , ec.value(), ec.message().c_str()); +++ return; +++ } +++ m_bound_to = bind_ip; +++ m_user_bound_to = bind_ip; +++ m_channel = c; +++ assert(m_forwarder); +++ c->hops[1].replace_last(m_forwarder); +++ } +++ +++ void tcp::socket::bind(ip::tcp::endpoint const& ep +++ , boost::system::error_code& ec) try +++ { +++ if (!m_open) +++ { +++ ec = error::bad_descriptor; +++ return; +++ } +++ +++ if (ep.address().is_v4() != m_is_v4) +++ { +++ ec = error::address_family_not_supported; +++ return; +++ } +++ +++ ip::tcp::endpoint addr = m_io_service.bind_socket(this, ep, ec); +++ if (ec) return; +++ m_bound_to = addr; +++ m_user_bound_to = addr; +++ } +++ catch (std::bad_alloc const&) +++ { +++ ec = make_error_code(boost::system::errc::not_enough_memory); +++ } +++ catch (boost::system::system_error const& err) +++ { +++ ec = err.code(); +++ } +++ +++ void tcp::socket::bind(ip::tcp::endpoint const& ep) +++ { +++ boost::system::error_code ec; +++ bind(ep, ec); +++ if (ec) throw boost::system::system_error(ec); +++ } +++ +++ void tcp::socket::close() +++ { +++ boost::system::error_code ec; +++ close(ec); +++ if (ec) throw boost::system::system_error(ec); +++ } +++ +++ void tcp::socket::close(boost::system::error_code& ec) try +++ { +++ if (m_channel) +++ { +++ int const remote = m_channel->remote_idx(m_bound_to); +++ route hops = m_channel->hops[remote]; +++ +++ // if m_connect_handler is still set, it means the connection hasn't +++ // been established yet, and this channel points to the acceptor +++ // socket, not another open TCP connection. +++ if (!hops.empty() && !m_connect_handler) +++ { +++ aux::packet p; +++ p.type = aux::packet::type_t::error; +++ p.ec = asio::error::eof; +++ p.from = asio::ip::udp::endpoint( +++ m_bound_to.address(), m_bound_to.port()); +++ p.overhead = 40; +++ p.hops = hops; +++ p.seq_nr = m_next_outgoing_seq++; +++ send_packet(std::move(p)); +++ } +++ m_channel.reset(); +++ } +++ +++ if (m_bound_to != ip::tcp::endpoint()) +++ { +++ m_io_service.unbind_socket(this, m_bound_to); +++ m_bound_to = ip::tcp::endpoint(); +++ m_user_bound_to = ip::tcp::endpoint(); +++ } +++ m_open = false; +++ +++ // prevent any more packets from being delivered to this socket +++ if (m_forwarder) +++ { +++ m_forwarder->reset(); +++ m_forwarder.reset(); +++ } +++ +++ // reset socket state +++ m_queue_size = 0; +++ m_mss = 1475; +++ m_cwnd = m_mss * 2; +++ m_bytes_in_flight = 0; +++ m_outstanding_packet_sizes.clear(); +++ m_recv_null_buffers = false; +++ m_send_null_buffers = false; +++ m_next_incoming_seq = 0; +++ m_next_outgoing_seq = 0; +++ m_last_drop_seq = 0; +++ +++ cancel(ec); +++ +++ ec.clear(); +++ } +++ catch (std::bad_alloc const&) +++ { +++ ec = make_error_code(boost::system::errc::not_enough_memory); +++ } +++ catch (boost::system::system_error const& err) +++ { +++ ec = err.code(); +++ } +++ +++ std::size_t tcp::socket::available(boost::system::error_code& ec) const +++ { +++ if (!m_open) +++ { +++ ec = boost::system::error_code(error::bad_descriptor); +++ return 0; +++ } +++ if (!m_channel) +++ { +++ ec = boost::system::error_code(error::not_connected); +++ return 0; +++ } +++ if (m_incoming_queue.empty()) +++ { +++ return 0; +++ } +++ +++ std::size_t ret = 0; +++ for (aux::packet const& p : m_incoming_queue) +++ { +++ if (p.type == aux::packet::type_t::error) +++ { +++ if (ret > 0) return ret; +++ +++ // if the read buffer is drained and there is an error, report that +++ // error. +++ ec = p.ec; +++ return 0; +++ } +++ ret += p.buffer.size(); +++ } +++ return ret; +++ } +++ +++ std::size_t tcp::socket::available() const +++ { +++ boost::system::error_code ec; +++ std::size_t ret = available(ec); +++ if (ec) throw boost::system::system_error(ec); +++ return ret; +++ } +++ +++ void tcp::socket::cancel(boost::system::error_code&) +++ { +++ abort_recv_handlers(); +++ abort_send_handlers(); +++ +++ if (m_connect_handler) +++ { +++ post(m_io_service, aux::make_malloc(std::bind(std::move(m_connect_handler) +++ , boost::system::error_code(error::operation_aborted)))); +++ m_connect_handler = nullptr; +++ } +++ } +++ +++ void tcp::socket::cancel() +++ { +++ boost::system::error_code ec; +++ cancel(ec); +++ if (ec) throw boost::system::system_error(ec); +++ } +++ +++ tcp::endpoint tcp::socket::remote_endpoint(boost::system::error_code& ec) const +++ { +++ if (!m_open) +++ { +++ ec = error::bad_descriptor; +++ return tcp::endpoint(); +++ } +++ +++ if (!m_channel) +++ { +++ ec = error::not_connected; +++ return tcp::endpoint(); +++ } +++ +++ int const remote = m_channel->remote_idx(m_bound_to); +++ return m_channel->visible_ep[remote]; +++ } +++ +++ tcp::endpoint tcp::socket::remote_endpoint() const +++ { +++ boost::system::error_code ec; +++ tcp::endpoint ret = remote_endpoint(ec); +++ if (ec) throw boost::system::system_error(ec); +++ return ret; +++ } +++ +++ void tcp::socket::async_connect(tcp::endpoint const& target +++ , aux::function h) +++ { +++ if (!m_open) open(target.protocol()); +++ +++ assert(h); +++ assert(!m_connect_handler); +++ +++ // find remote socket +++ boost::system::error_code ec; +++ if (m_bound_to.address() == ip::address()) +++ { +++ auto endpoint = ip::tcp::endpoint(); +++ if (target.address().is_v4()) { +++ endpoint.address(ip::address_v4::any()); +++ } else { +++ endpoint.address(ip::address_v6::any()); +++ } +++ ip::tcp::endpoint addr = m_io_service.bind_socket(this +++ , endpoint, ec); +++ if (ec) +++ { +++ post(m_io_service, aux::make_malloc(std::bind(std::move(h), ec))); +++ return; +++ } +++ m_bound_to = addr; +++ m_user_bound_to = addr; +++ } +++ if (m_bound_to.address().is_v4() != target.address().is_v4()) +++ { +++ post(m_io_service, aux::make_malloc(std::bind(std::move(h), +++ boost::system::error_code(error::address_family_not_supported)))); +++ return; +++ } +++ m_channel = m_io_service.internal_connect(this, target, ec); +++ m_mss = m_io_service.get_path_mtu(m_bound_to.address(), target.address()); +++ m_cwnd = m_mss * 2; +++ if (ec) +++ { +++ m_channel.reset(); +++ // TODO: ask the policy object what the round-trip to this endpoint is +++ m_connect_timer.expires_after(chrono::milliseconds(50)); +++ m_connect_timer.async_wait(aux::make_malloc(std::bind(std::move(h), ec))); +++ return; +++ } +++ +++ m_connect_handler = std::move(h); +++ +++ // the acceptor socket will call internal_connect_complete once the +++ // connection is established +++ } +++ +++ void tcp::socket::abort_recv_handlers() +++ { +++ if (m_recv_handler) post(m_io_service, aux::make_malloc(std::bind(std::move(m_recv_handler) +++ , boost::system::error_code(error::operation_aborted), std::size_t(0)))); +++ +++ if (m_wait_recv_handler) post(m_io_service, aux::make_malloc(std::bind(std::move(m_wait_recv_handler) +++ , boost::system::error_code(error::operation_aborted)))); +++ +++ m_recv_timer.cancel(); +++ m_recv_handler = nullptr; +++ m_wait_recv_handler = nullptr; +++ m_recv_buffer.clear(); +++ m_recv_null_buffers = false; +++ } +++ +++ void tcp::socket::abort_send_handlers() +++ { +++ if (m_send_handler) post(m_io_service, aux::make_malloc(std::bind(std::move(m_send_handler) +++ , boost::system::error_code(error::operation_aborted), std::size_t(0)))); +++ +++ if (m_wait_send_handler) post(m_io_service, aux::make_malloc(std::bind(std::move(m_wait_send_handler) +++ , boost::system::error_code(error::operation_aborted)))); +++ +++ m_send_handler = nullptr; +++ m_wait_send_handler = nullptr; +++ m_send_buffer.clear(); +++ m_send_null_buffers = false; +++ } +++ +++ void tcp::socket::async_write_some_impl(std::vector const& bufs +++ , aux::function handler) +++ { +++ boost::system::error_code ec; +++ std::size_t const bytes_transferred = write_some_impl(bufs, ec); +++ if (ec == boost::system::error_code(error::would_block)) +++ { +++ m_send_handler = std::move(handler); +++ m_send_buffer = bufs; +++ return; +++ } +++ +++ if (ec) +++ { +++ post(m_io_service, aux::make_malloc(std::bind(std::move(handler), ec, std::size_t(0)))); +++ m_send_handler = nullptr; +++ m_send_buffer.clear(); +++ return; +++ } +++ +++ boost::system::error_code no_error; +++ post(m_io_service, aux::make_malloc(std::bind(std::move(handler), no_error +++ , bytes_transferred))); +++ m_send_handler = nullptr; +++ m_send_buffer.clear(); +++ } +++ +++ std::size_t tcp::socket::write_some_impl( +++ std::vector const& bufs +++ , boost::system::error_code& ec) +++ { +++ if (!m_open) +++ { +++ ec = boost::system::error_code(error::bad_descriptor); +++ return 0; +++ } +++ if (!m_channel) +++ { +++ ec = boost::system::error_code(error::not_connected); +++ return 0; +++ } +++ +++ // the connect handler is used as proxy that this socket has not competed +++ // the connection yet. We're still waiting for SYN+ACK +++ if (m_connect_handler) +++ { +++ ec = boost::system::error_code(error::would_block); +++ return 0; +++ } +++ +++ int const remote = m_channel->remote_idx(m_bound_to); +++ route hops = m_channel->hops[remote]; +++ if (hops.empty()) +++ { +++ ec = boost::system::error_code(error::not_connected); +++ return 0; +++ } +++ +++ if (m_bytes_in_flight + m_mss > m_cwnd) +++ { +++ // this indicates that the send buffer is very large, we should +++ // probably not be able to stuff more bytes down it +++ // wait for the receiving end to pop some bytes off +++ ec = boost::system::error_code(error::would_block); +++ return 0; +++ } +++ +++ std::size_t ret = 0; +++ +++ for (auto const& buf : bufs) +++ { +++ // split up in packets +++ int buf_size = int(buf.size()); +++ std::uint8_t const* ptr = static_cast(buf.data()); +++ while (buf_size > 0) +++ { +++ int packet_size = (std::min)(buf_size, m_mss); +++ aux::packet p; +++ p.type = aux::packet::type_t::payload; +++ p.buffer.assign(ptr, ptr + packet_size); +++ p.from = asio::ip::udp::endpoint( +++ m_bound_to.address(), m_bound_to.port()); +++ p.overhead = 40; +++ p.hops = hops; +++ p.seq_nr = m_next_outgoing_seq++; +++ p.drop_fun = std::bind(&tcp::socket::packet_dropped, this, _1); +++ +++ send_packet(std::move(p)); +++ ptr += packet_size; +++ buf_size -= packet_size; +++ ret += packet_size; +++ +++ if (m_bytes_in_flight + m_mss > m_cwnd) +++ { +++ // the congestion window is full +++ if (ret == 0) +++ { +++ ec = boost::system::error_code(error::would_block); +++ return 0; +++ } +++ +++ return ret; +++ } +++ } +++ } +++ +++ return ret; +++ } +++ +++ std::size_t tcp::socket::read_some_impl( +++ std::vector const& bufs +++ , boost::system::error_code& ec) +++ { +++ // a zero-sized read always completes immediately with 0 bytes; +++ // boost.asio's SSL implementation relies on this to post a +++ // completion handler without transferring any data. +++ if (boost::asio::buffer_size(bufs) == 0) +++ { +++ ec.clear(); +++ return 0; +++ } +++ +++ assert(!bufs.empty()); +++ if (!m_open) +++ { +++ ec = boost::system::error_code(error::bad_descriptor); +++ return 0; +++ } +++ if (!m_channel) +++ { +++ ec = boost::system::error_code(error::not_connected); +++ return 0; +++ } +++ if (m_connect_handler) +++ { +++ // the socket is not done connecting yet +++ ec = boost::system::error_code(error::would_block); +++ return 0; +++ } +++ +++ if (m_incoming_queue.empty()) +++ { +++ ec = boost::system::error_code(error::would_block); +++ return 0; +++ } +++ +++ typedef std::vector buffers_t; +++ m_recv_buffer = bufs; +++ buffers_t::iterator recv_iter = m_recv_buffer.begin(); +++ int total_received = 0; +++ // the offset in the current receive buffer we're writing to. i.e. the +++ // buffer recv_iter points to +++ int buf_offset = 0; +++ +++ while (!m_incoming_queue.empty()) +++ { +++ aux::packet& p = m_incoming_queue.front(); +++ +++ if (p.type == aux::packet::type_t::error) +++ { +++ // if we have received bytes also, first deliver those. In the next +++ // read, deliver the error +++ if (total_received > 0) break; +++ +++ assert(p.ec); +++ ec = p.ec; +++ m_incoming_queue.erase(m_incoming_queue.begin()); +++ m_channel.reset(); +++ return 0; +++ } +++ else if (p.type == aux::packet::type_t::payload) +++ { +++ // copy bytes from the incoming queue into the receive buffer. +++ // both are vectors of buffer, so it can get a bit hairy +++ while (recv_iter != m_recv_buffer.end()) +++ { +++ int const buf_size = int(recv_iter->size()); +++ int const copy_size = (std::min)(int(p.buffer.size()) +++ , buf_size - buf_offset); +++ +++ memcpy(static_cast(recv_iter->data()) + buf_offset +++ , p.buffer.data(), copy_size); +++ +++ p.buffer.erase(p.buffer.begin(), p.buffer.begin() + copy_size); +++ m_queue_size -= copy_size; +++ +++ buf_offset += copy_size; +++ assert(buf_offset <= buf_size); +++ total_received += copy_size; +++ if (buf_offset == buf_size) +++ { +++ ++recv_iter; +++ buf_offset = 0; +++ } +++ +++ if (p.buffer.empty()) +++ { +++ m_incoming_queue.erase(m_incoming_queue.begin()); +++ break; +++ } +++ } +++ } +++ else +++ { +++ assert(false); +++ } +++ +++ if (recv_iter == m_recv_buffer.end()) +++ break; +++ } +++ +++ assert(total_received > 0); +++ +++ ec.clear(); +++ return total_received; +++ } +++ +++ void tcp::socket::async_read_some_impl(std::vector const& bufs +++ , aux::function handler) +++ { +++ // a zero-sized read always completes immediately with 0 bytes +++ // transferred, regardless of connection or queue state. boost.asio's +++ // SSL implementation relies on this to bounce a completion handler +++ // through the executor without actually transferring any data. +++ if (boost::asio::buffer_size(bufs) == 0) +++ { +++ post(m_io_service, aux::make_malloc(std::bind(std::move(handler) +++ , boost::system::error_code(), std::size_t(0)))); +++ return; +++ } +++ +++ assert(!bufs.empty()); +++ +++ boost::system::error_code ec; +++ std::size_t bytes_transferred = read_some_impl(bufs, ec); +++ if (ec == boost::system::error_code(error::would_block)) +++ { +++ assert(m_incoming_queue.empty()); +++ +++ m_recv_buffer = bufs; +++ m_recv_handler = std::move(handler); +++ m_recv_null_buffers = false; +++ return; +++ } +++ +++ if (ec) +++ { +++ post(m_io_service, aux::make_malloc(std::bind(std::move(handler), ec, std::size_t(0)))); +++ m_recv_handler = nullptr; +++ m_recv_buffer.clear(); +++ return; +++ } +++ +++ post(m_io_service, aux::make_malloc(std::bind(std::move(handler), ec, bytes_transferred))); +++ m_recv_handler = nullptr; +++ m_recv_buffer.clear(); +++ } +++ +++ void tcp::socket::async_wait_read_impl( +++ aux::function handler) +++ { +++ boost::system::error_code ec; +++ // null_buffers notifies the handler when data is available, without +++ // reading any +++ int const bytes = int(available(ec)); +++ if (ec) +++ { +++ post(m_io_service, aux::make_malloc(std::bind(std::move(handler), ec))); +++ m_recv_handler = nullptr; +++ m_recv_buffer.clear(); +++ return; +++ } +++ +++ if (bytes > 0) +++ { +++ post(m_io_service, aux::make_malloc(std::bind(std::move(handler), ec))); +++ m_recv_handler = nullptr; +++ m_recv_buffer.clear(); +++ return; +++ } +++ +++ m_wait_recv_handler = std::move(handler); +++ m_recv_null_buffers = true; +++ } +++ +++ void tcp::socket::async_wait_write_impl( +++ aux::function handler) +++ { +++ if (!m_open) +++ { +++ boost::system::error_code const ec(error::bad_descriptor); +++ post(m_io_service, aux::make_malloc(std::bind(std::move(handler), ec))); +++ return; +++ } +++ +++ if (!m_channel) +++ { +++ boost::system::error_code const ec(error::not_connected); +++ post(m_io_service, aux::make_malloc(std::bind(std::move(handler), ec))); +++ return; +++ } +++ +++ if (!m_connect_handler && m_bytes_in_flight + m_mss <= m_cwnd) +++ { +++ // the socket is writable right now. complete immediately without +++ // actually writing anything, this is the null_buffers equivalent +++ // for writes +++ boost::system::error_code const ec; +++ post(m_io_service, aux::make_malloc(std::bind(std::move(handler), ec))); +++ return; +++ } +++ +++ m_wait_send_handler = std::move(handler); +++ m_send_null_buffers = true; +++ } +++ +++ // if there is an outstanding read operation, and this was the first incoming +++ // operation since we last drained, wake up the reader +++ void tcp::socket::maybe_wakeup_reader() +++ { +++ if (m_incoming_queue.size() != 1 || (!m_recv_handler && !m_wait_recv_handler)) return; +++ +++ if (m_recv_null_buffers) +++ { +++ async_wait_read_impl(std::move(m_wait_recv_handler)); +++ } +++ else +++ { +++ // we have an async. read operation outstanding, and we just put one +++ // packet in our incoming queue. +++ +++ // try to read from it and potentially fire the handler +++ async_read_some_impl(m_recv_buffer, std::move(m_recv_handler)); +++ } +++ } +++ +++ void tcp::socket::maybe_wakeup_writer() +++ { +++ if (!m_send_handler && !m_wait_send_handler) return; +++ +++ if (m_send_null_buffers) +++ { +++ async_wait_write_impl(std::move(m_wait_send_handler)); +++ } +++ else +++ { +++ // we have an async. write operation outstanding +++ async_write_some_impl(m_send_buffer, std::move(m_send_handler)); +++ } +++ } +++ +++ bool tcp::socket::internal_is_listening() { return false; } +++ +++ void tcp::socket::send_packet(aux::packet p) +++ { +++ m_bytes_in_flight += int(p.buffer.size()); +++ m_outstanding_packet_sizes[p.seq_nr] = int(p.buffer.size()); +++ +++ int const idx = m_channel->self_idx(m_bound_to); +++ p.byte_counter = m_channel->bytes_sent[idx]; +++ m_channel->bytes_sent[idx] += std::uint32_t(p.buffer.size()); +++ +++ auto* log = m_io_service.sim().get_pcap(); +++ if (log) +++ { +++ int const remote = m_channel->remote_idx(m_bound_to); +++ log->log_tcp(p, m_bound_to, m_channel->ep[remote]); +++ } +++ +++ forward_packet(std::move(p)); +++ } +++ +++ void tcp::socket::packet_dropped(aux::packet p) +++ { +++ int remote = m_channel->remote_idx(m_bound_to); +++ p.hops = m_channel->hops[remote]; +++ m_outgoing_packets.push_back(std::move(p)); +++ +++ const int packets_in_cwnd = m_cwnd / m_mss; +++ +++ // we just recently dropped a packet and cut the cwnd in half, +++ // don't do it again already +++ if (m_last_drop_seq > 0 && p.seq_nr < m_last_drop_seq + packets_in_cwnd) return; +++ +++ m_cwnd /= 2; +++ m_last_drop_seq = p.seq_nr; +++ +++ // TODO: this should really happen one second later to be accurate +++ if (m_cwnd < m_mss) m_cwnd = m_mss; +++ } +++ +++ void tcp::socket::incoming_packet(aux::packet p) +++ { +++ switch (p.type) +++ { +++ case aux::packet::type_t::uninitialized: +++ { +++ assert(false && "uninitialized packet"); +++ return; +++ } +++ case aux::packet::type_t::ack: +++ { +++ // if the socket just became writeable, we need to notify the +++ // client. First we want to know whether it was not writeable. +++ const bool was_writeable = m_bytes_in_flight + m_mss > m_cwnd; +++ +++ auto it = m_outstanding_packet_sizes.find(p.seq_nr); +++ assert(it != m_outstanding_packet_sizes.end()); +++ const int acked_bytes = it->second; +++ m_outstanding_packet_sizes.erase(it); +++ assert(m_bytes_in_flight >= acked_bytes); +++ m_bytes_in_flight -= acked_bytes; +++ +++ // potentially resend packets +++ while (!m_outgoing_packets.empty() +++ && m_bytes_in_flight +++ + int(m_outgoing_packets.front().buffer.size()) <= m_cwnd) +++ { +++ aux::packet pkt = std::move(m_outgoing_packets.front()); +++ m_outgoing_packets.erase(m_outgoing_packets.begin()); +++ send_packet(std::move(pkt)); +++ } +++ +++ // update cwnd based on the number of bytes ACKed. +++ // every round-trip, increase the window size by one packet +++ // (MSS) +++ m_cwnd += m_mss * acked_bytes / m_cwnd; +++ +++ // TODO: implement slow-start +++ +++ const bool is_writeable = m_bytes_in_flight + m_mss <= m_cwnd; +++ +++ if (!was_writeable && is_writeable) +++ maybe_wakeup_writer(); +++ +++ return; +++ } +++ case aux::packet::type_t::syn: +++ { +++ // TODO: return connection refused +++ return; +++ } +++ case aux::packet::type_t::syn_ack: +++ { +++ assert(m_connect_handler); +++ boost::system::error_code ec; +++ post(m_io_service, aux::make_malloc(std::bind(std::move(m_connect_handler), ec))); +++ m_connect_handler = nullptr; +++ if (ec) m_channel.reset(); +++ else maybe_wakeup_writer(); +++ return; +++ } +++ case aux::packet::type_t::error: +++ case aux::packet::type_t::payload: +++ { +++ aux::packet ack; +++ ack.type = aux::packet::type_t::ack; +++ ack.seq_nr = p.seq_nr; +++ +++ int remote = m_channel->remote_idx(m_bound_to); +++ ack.hops = m_channel->hops[remote]; +++ forward_packet(std::move(ack)); +++ +++ // if the sequence number is out-of-order, put it in the +++ // m_incoming_packets queue +++ if (p.seq_nr != m_next_incoming_seq) +++ { +++ if (p.seq_nr < m_next_incoming_seq) +++ { +++ std::printf("TCP: incoming sequence number lower (%" PRId64 ") " +++ "than expected: %" PRId64 "\n", p.seq_nr, m_next_incoming_seq); +++ } +++ +++ m_reorder_buffer.emplace(p.seq_nr, std::move(p)); +++ return; +++ } +++ +++ // this packet was in-order. increment the expected next sequence +++ // number. +++ ++m_next_incoming_seq; +++ m_incoming_queue.push_back(std::move(p)); +++ +++ // also, perhaps there are some packets that arrived out-of-order, +++ // check to see +++ auto it = m_reorder_buffer.find(m_next_incoming_seq); +++ while (it != m_reorder_buffer.end()) +++ { +++ aux::packet pkt = std::move(it->second); +++ m_reorder_buffer.erase(it); +++ m_incoming_queue.push_back(std::move(pkt)); +++ ++m_next_incoming_seq; +++ it = m_reorder_buffer.find(m_next_incoming_seq); +++ } +++ +++ maybe_wakeup_reader(); +++ return; +++ } +++ } +++ } +++} +++} +++} +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/udp_socket.cpp ++@@ -0,0 +1,492 @@ +++/* +++ +++Copyright (c) 2015, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#include "simulator/simulator.hpp" +++#include "simulator/packet.hpp" +++#include "simulator/pcap.hpp" +++#include "simulator/handler_allocator.hpp" +++ +++#include +++ +++#include "simulator/push_warnings.hpp" +++#include +++#include +++#include "simulator/pop_warnings.hpp" +++ +++typedef sim::chrono::high_resolution_clock::time_point time_point; +++typedef sim::chrono::high_resolution_clock::duration duration; +++ +++namespace sim { +++namespace asio { +++namespace ip { +++ +++ udp::socket::socket(io_context& ios) +++ : socket_base(ios) +++ , m_next_send(chrono::high_resolution_clock::now()) +++ , m_recv_sender(nullptr) +++ , m_recv_timer(ios) +++ , m_send_timer(ios) +++ , m_recv_null_buffers(0) +++ , m_queue_size(0) +++ , m_is_v4(true) +++ {} +++ +++ udp::socket::socket(socket&& s) +++ : socket_base(std::move(s)) +++ , m_next_send(std::move(s.m_next_send)) +++ , m_send_handler(std::move(s.m_send_handler)) +++ , m_wait_send_handler(std::move(s.m_wait_send_handler)) +++ , m_recv_handler(std::move(s.m_recv_handler)) +++ , m_wait_recv_handler(std::move(s.m_wait_recv_handler)) +++ , m_recv_buffer(std::move(s.m_recv_buffer)) +++ , m_recv_sender(std::move(s.m_recv_sender)) +++ , m_recv_timer(std::move(s.m_recv_timer)) +++ , m_send_timer(std::move(s.m_send_timer)) +++ , m_incoming_queue(std::move(s.m_incoming_queue)) +++ , m_recv_null_buffers(std::move(s.m_recv_null_buffers)) +++ , m_queue_size(std::move(s.m_queue_size)) +++ , m_is_v4(s.m_is_v4) +++ { +++ if (m_forwarder) m_forwarder->reset(this); +++ s.m_forwarder.reset(); +++ s.m_open = false; +++ s.m_bound_to = ip::udp::endpoint(); +++ if (m_bound_to != ip::udp::endpoint()) +++ m_io_service.rebind_udp_socket(this, m_bound_to); +++ } +++ +++ udp::socket::~socket() +++ { +++ boost::system::error_code ec; +++ close(ec); +++ } +++ +++ void udp::socket::bind(ip::udp::endpoint const& ep +++ , boost::system::error_code& ec) try +++ { +++ if (!m_open) +++ { +++ ec = error::bad_descriptor; +++ return; +++ } +++ +++ if (ep.address().is_v4() != m_is_v4) +++ { +++ ec = error::address_family_not_supported; +++ return; +++ } +++ +++ ip::udp::endpoint addr = m_io_service.bind_udp_socket(this, ep, ec); +++ if (ec) return; +++ m_bound_to = addr; +++ m_user_bound_to = addr; +++ } +++ catch (std::bad_alloc const&) +++ { +++ ec = make_error_code(boost::system::errc::not_enough_memory); +++ } +++ catch (boost::system::system_error const& err) +++ { +++ ec = err.code(); +++ } +++ +++ void udp::socket::bind(ip::udp::endpoint const& ep) +++ { +++ boost::system::error_code ec; +++ bind(ep, ec); +++ if (ec) throw boost::system::system_error(ec); +++ } +++ +++ void udp::socket::open(udp protocol +++ , boost::system::error_code& ec) try +++ { +++ // TODO: what if it's already open? +++ close(ec); +++ m_open = true; +++ m_is_v4 = (protocol == ip::udp::v4()); +++ m_forwarder = std::make_shared(this); +++ } +++ catch (std::bad_alloc const&) +++ { +++ ec = make_error_code(boost::system::errc::not_enough_memory); +++ } +++ catch (boost::system::system_error const& err) +++ { +++ ec = err.code(); +++ } +++ +++ void udp::socket::open(udp protocol) +++ { +++ boost::system::error_code ec; +++ open(protocol, ec); +++ if (ec) throw boost::system::system_error(ec); +++ } +++ +++ void udp::socket::close() +++ { +++ boost::system::error_code ec; +++ close(ec); +++ if (ec) throw boost::system::system_error(ec); +++ } +++ +++ void udp::socket::close(boost::system::error_code& ec) try +++ { +++ if (m_bound_to != ip::udp::endpoint()) +++ { +++ m_io_service.unbind_udp_socket(this, m_bound_to); +++ m_bound_to = ip::udp::endpoint(); +++ m_user_bound_to = ip::udp::endpoint(); +++ } +++ m_open = false; +++ +++ // prevent any more packets from being delivered to this socket +++ if (m_forwarder) +++ { +++ m_forwarder->reset(); +++ m_forwarder.reset(); +++ } +++ +++ cancel(ec); +++ } +++ catch (std::bad_alloc const&) +++ { +++ ec = make_error_code(boost::system::errc::not_enough_memory); +++ } +++ catch (boost::system::system_error const& err) +++ { +++ ec = err.code(); +++ } +++ +++ void udp::socket::cancel(boost::system::error_code&) +++ { +++ // cancel outstanding async operations +++ abort_recv_handlers(); +++ abort_send_handlers(); +++ m_recv_timer.cancel(); +++ m_send_timer.cancel(); +++ return; +++ } +++ +++ void udp::socket::cancel() +++ { +++ boost::system::error_code ec; +++ cancel(ec); +++ if (ec) throw boost::system::system_error(ec); +++ } +++ +++ void udp::socket::abort_send_handlers() +++ { +++ if (m_send_handler) +++ post(m_io_service, make_malloc(std::bind(std::ref(m_send_handler) +++ , boost::system::error_code(error::operation_aborted), std::size_t(0)))); +++ +++ if (m_wait_send_handler) +++ post(m_io_service, make_malloc(std::bind(std::ref(m_wait_send_handler) +++ , boost::system::error_code(error::operation_aborted)))); +++ +++ m_send_timer.cancel(); +++ m_send_handler = nullptr; +++ m_wait_send_handler = nullptr; +++// m_send_buffer.clear(); +++ } +++ +++ void udp::socket::abort_recv_handlers() +++ { +++ if (m_recv_handler) +++ post(m_io_service, make_malloc(std::bind(std::move(m_recv_handler) +++ , boost::system::error_code(error::operation_aborted), std::size_t(0)))); +++ +++ if (m_wait_recv_handler) +++ post(m_io_service, make_malloc(std::bind(std::move(m_wait_recv_handler) +++ , boost::system::error_code(error::operation_aborted)))); +++ +++ m_recv_timer.cancel(); +++ m_recv_handler = nullptr; +++ m_wait_recv_handler = nullptr; +++ m_recv_buffer.clear(); +++ } +++ +++ void udp::socket::async_wait(socket_base::wait_type_t const w +++ , aux::function handler) +++ { +++ if (w == wait_type_t::wait_write) +++ { +++ abort_send_handlers(); +++ +++ time_point const now = chrono::high_resolution_clock::now(); +++ boost::system::error_code no_error; +++ if (m_next_send - now > m_send_queue_time / 2) +++ { +++ // our send queue is too large. Defer +++ m_recv_timer.expires_at(m_next_send + m_send_queue_time / 2); +++ +++ m_wait_send_handler = std::move(handler); +++ m_recv_timer.async_wait(make_malloc(std::bind(std::ref(m_wait_send_handler), no_error))); +++ return; +++ } +++ +++ // the socket is writable, post the completion handler immediately +++ post(m_io_service, make_malloc(std::bind(std::move(handler), no_error))); +++ } +++ else if (w == wait_type_t::wait_read) +++ { +++ abort_recv_handlers(); +++ async_wait_receive_impl(nullptr, std::move(handler)); +++ } +++ } +++ +++ std::size_t udp::socket::receive_from_impl( +++ std::vector const& bufs +++ , udp::endpoint* sender +++ , socket_base::message_flags /* flags */ +++ , boost::system::error_code& ec) +++ { +++ assert(!bufs.empty()); +++ if (!m_open) +++ { +++ ec = boost::system::error_code(error::bad_descriptor); +++ return 0; +++ } +++ +++ if (m_bound_to == udp::endpoint()) +++ { +++ ec = boost::system::error_code(error::invalid_argument); +++ return 0; +++ } +++ +++ if (m_incoming_queue.empty()) +++ { +++ ec = boost::system::error_code(error::would_block); +++ return 0; +++ } +++ +++ aux::packet& p = m_incoming_queue.front(); +++ if (sender) *sender = p.from; +++ +++ int read = 0; +++ for (auto const& buf : bufs) +++ { +++ char* ptr = static_cast(buf.data()); +++ int const len = int(buf.size()); +++ int const to_copy = (std::min)(int(p.buffer.size()), len); +++ memcpy(ptr, p.buffer.data(), to_copy); +++ read += to_copy; +++ p.buffer.erase(p.buffer.begin(), p.buffer.begin() + to_copy); +++ m_queue_size -= to_copy; +++ if (p.buffer.empty()) break; +++ } +++ +++ m_incoming_queue.erase(m_incoming_queue.begin()); +++ return read; +++ } +++ +++ void udp::socket::async_wait_receive_impl( +++ udp::endpoint* sender +++ , aux::function handler) +++ { +++ if (!m_open) +++ { +++ post(m_io_service, make_malloc(std::bind(std::move(handler) +++ , boost::system::error_code(error::bad_descriptor)))); +++ return; +++ } +++ +++ if (m_bound_to == udp::endpoint()) +++ { +++ post(m_io_service, make_malloc(std::bind(std::move(handler) +++ , boost::system::error_code(error::invalid_argument)))); +++ return; +++ } +++ +++ if (!m_incoming_queue.empty()) +++ { +++ post(m_io_service, make_malloc(std::bind(std::move(handler), boost::system::error_code()))); +++ return; +++ } +++ +++ m_recv_null_buffers = true; +++ m_wait_recv_handler = std::move(handler); +++ m_recv_sender = sender; +++ } +++ +++ void udp::socket::async_receive_from_impl( +++ std::vector const& bufs +++ , udp::endpoint* sender +++ , socket_base::message_flags /* flags */ +++ , aux::function handler) +++ { +++ assert(!bufs.empty()); +++ +++ boost::system::error_code ec; +++ std::size_t bytes_transferred = receive_from_impl(bufs, sender, 0, ec); +++ if (ec == boost::system::error_code(error::would_block)) +++ { +++ m_recv_buffer = bufs; +++ m_recv_handler = std::move(handler); +++ m_recv_sender = sender; +++ m_recv_null_buffers = false; +++ +++ return; +++ } +++ +++ if (ec) +++ { +++ post(m_io_service, make_malloc(std::bind(std::move(handler), ec, std::size_t(0)))); +++ m_recv_handler = nullptr; +++ m_recv_buffer.clear(); +++ m_recv_sender = nullptr; +++ m_recv_null_buffers = false; +++ return; +++ } +++ +++ post(m_io_service, make_malloc(std::bind(std::move(handler), ec, bytes_transferred))); +++ m_recv_handler = nullptr; +++ m_recv_buffer.clear(); +++ m_recv_sender = nullptr; +++ m_recv_null_buffers = false; +++ } +++ +++ std::size_t udp::socket::send_to_impl(std::vector const& b +++ , udp::endpoint const& dst, message_flags /* flags */ +++ , boost::system::error_code& ec) +++ { +++ assert(m_non_blocking && "blocking operations not supported"); +++ +++ if (m_bound_to == ip::udp::endpoint()) +++ { +++ // the socket was not bound, bind to anything +++ bind(udp::endpoint(), ec); +++ if (ec) return 0; +++ } +++ +++ ec.clear(); +++ std::size_t ret = 0; +++ for (std::vector::const_iterator i = b.begin() +++ , end(b.end()); i != end; ++i) +++ { +++ ret += i->size(); +++ } +++ if (ret == 0) +++ { +++ ec = boost::system::error_code(error::invalid_argument); +++ return 0; +++ } +++ +++ time_point now = chrono::high_resolution_clock::now(); +++ +++ const int mtu = m_io_service.get_path_mtu(m_bound_to.address(), dst.address()); +++ +++ if (int(ret) > 65535) +++ { +++ ec = boost::system::error_code(error::message_size); +++ return 0; +++ } +++ +++ if (m_dont_fragment && int(ret) > mtu) +++ { +++ // silently drop packet +++ ec.clear(); +++ return ret; +++ } +++ +++ // determine the bandwidth in terms of nanoseconds / byte +++ const double nanoseconds_per_byte = 1000000000.0 +++ / double(aux::nic_bandwidth); +++ +++ if (m_next_send - now > m_send_queue_time) +++ { +++ // our send queue is too large. +++ ec = boost::system::error_code(asio::error::would_block); +++ return 0; +++ } +++ +++ route hops = m_io_service.find_udp_socket(*this, dst); +++ if (hops.empty()) +++ { +++ // the packet is silently dropped +++ // TODO: it would be nice if this would result in a round-trip time +++ // with an ICMP host unreachable or connection_refused error +++ return ret; +++ } +++ +++ hops.prepend(m_io_service.get_outgoing_route(m_bound_to.address())); +++ +++ m_next_send = std::max(now, m_next_send); +++ +++ aux::packet p; +++ p.overhead = 28; +++ p.type = aux::packet::type_t::payload; +++ p.from = m_bound_to; +++ p.hops = hops; +++ for (std::vector::const_iterator i = b.begin() +++ , end(b.end()); i != end; ++i) +++ { +++ p.buffer.insert(p.buffer.end(), static_cast(i->data()) +++ , static_cast(i->data()) + i->size()); +++ } +++ +++ auto* log = m_io_service.sim().get_pcap(); +++ if (log) log->log_udp(p, m_bound_to, dst); +++ +++ int const packet_size = int(p.buffer.size() + p.overhead); +++ forward_packet(std::move(p)); +++ +++ m_next_send += chrono::duration_cast(chrono::nanoseconds( +++ boost::int64_t(nanoseconds_per_byte * packet_size))); +++ +++ return ret; +++ } +++ +++ void udp::socket::incoming_packet(aux::packet p) +++ { +++ int const packet_size = int(p.buffer.size() + p.overhead); +++ +++ // silent drop. If the application isn't reading fast enough, drop packets +++ // TODO: make this limit controlled by SO_RECVBUF socket option +++ if (m_queue_size + packet_size > 256 * 1024) return; +++ +++ m_queue_size += int(p.buffer.size()); +++ m_incoming_queue.push_back(std::move(p)); +++ +++ maybe_wakeup_reader(); +++ } +++ +++ void udp::socket::maybe_wakeup_reader() +++ { +++ if (m_incoming_queue.size() != 1 || (!m_recv_handler && !m_wait_recv_handler)) return; +++ +++ // there is an outstanding operation waiting for an incoming packet +++ if (m_recv_null_buffers) +++ { +++ async_wait_receive_impl(m_recv_sender, std::move(m_wait_recv_handler)); +++ } +++ else +++ { +++ async_receive_from_impl(m_recv_buffer, m_recv_sender, 0, std::move(m_recv_handler)); +++ } +++ +++// m_recv_handler = nullptr; +++// m_recv_buffer.clear(); +++// m_recv_sender = nullptr; +++ } +++ +++} // ip +++} // asio +++} // sim +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/test/acceptor.cpp ++@@ -0,0 +1,183 @@ +++/* +++ +++Copyright (c) 2015, Arvid Norberg +++All rights reserved. +++ +++ This program is free software: you can redistribute it and/or modify +++ it under the terms of the GNU General Public License as published by +++ the Free Software Foundation, either version 3 of the License, or +++ (at your option) any later version. +++ This program is distributed in the hope that it will be useful, +++ but WITHOUT ANY WARRANTY; without even the implied warranty of +++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +++ GNU General Public License for more details. +++ You should have received a copy of the GNU General Public License +++ along with this program. If not, see . +++ +++*/ +++ +++#include "simulator/simulator.hpp" +++#include +++ +++#include "catch.hpp" +++ +++#ifdef __GNUC__ +++// for CATCH's CHECK macro +++#pragma GCC diagnostic ignored "-Wparentheses" +++#endif +++ +++using namespace sim::asio; +++using namespace sim::chrono; +++using sim::simulation; +++using sim::default_config; +++using namespace std::placeholders; +++ +++namespace { +++char send_buffer[10000]; +++char recv_buffer[10000]; +++int num_received = 0; +++int num_sent = 0; +++ +++void on_sent(boost::system::error_code const& ec, std::size_t bytes_transferred +++ , ip::tcp::socket& sock) +++{ +++ int millis = int(duration_cast(high_resolution_clock::now() +++ .time_since_epoch()).count()); +++ if (ec) +++ { +++ std::printf("[%4d] send error %s\n", millis, ec.message().c_str()); +++ return; +++ } +++ +++ num_sent += int(bytes_transferred); +++ +++ std::printf("[%4d] sent %d bytes\n", millis, int(bytes_transferred)); +++ std::printf("closing\n"); +++ sock.close(); +++} +++ +++void on_receive(boost::system::error_code const& ec +++ , std::size_t bytes_transferred, ip::tcp::socket& sock) +++{ +++ int millis = int(duration_cast(high_resolution_clock::now() +++ .time_since_epoch()).count()); +++ if (ec) +++ { +++ std::printf("[%4d] receive error %s\n", millis, ec.message().c_str()); +++ return; +++ } +++ +++ num_received += int(bytes_transferred); +++ +++ std::printf("[%4d] received %d bytes\n", millis, int(bytes_transferred)); +++ +++ sock.async_read_some(sim::asio::buffer(recv_buffer, sizeof(recv_buffer)) +++ , std::bind(&on_receive, _1, _2, std::ref(sock))); +++} +++ +++void incoming_connection(boost::system::error_code const& ec +++ , ip::tcp::socket& sock, ip::tcp::endpoint const& ep) +++{ +++ int millis = int(duration_cast(high_resolution_clock::now() +++ .time_since_epoch()).count()); +++ if (ec) +++ { +++ std::printf("[%4d] error while accepting connection: %s\n" +++ , millis, ec.message().c_str()); +++ return; +++ } +++ +++ boost::system::error_code err; +++ ip::tcp::endpoint remote_endpoint = sock.remote_endpoint(err); +++ REQUIRE(!ec); +++ ip::tcp::endpoint local_endpoint = sock.local_endpoint(err); +++ REQUIRE(!ec); +++ std::printf("[%4d] received incoming connection from: %s:%d. local endpoint: %s:%d\n" +++ , millis, ep.address().to_string().c_str(), ep.port() +++ , local_endpoint.address().to_string().c_str(), local_endpoint.port()); +++ CHECK(local_endpoint.port() == 1337); +++ CHECK(local_endpoint.address().to_string() == "40.30.20.10"); +++ CHECK(remote_endpoint.port() != 0); +++ CHECK(remote_endpoint.address().to_string() == "10.20.30.40"); +++ +++ sock.async_read_some(sim::asio::buffer(recv_buffer, sizeof(recv_buffer)) +++ , std::bind(&on_receive, _1, _2, std::ref(sock))); +++} +++ +++void on_connected(boost::system::error_code const& ec +++ , ip::tcp::socket& sock) +++{ +++ int millis = int(duration_cast(high_resolution_clock::now() +++ .time_since_epoch()).count()); +++ if (ec) +++ { +++ std::printf("[%4d] error while connecting: %s\n", millis, ec.message().c_str()); +++ return; +++ } +++ +++ boost::system::error_code err; +++ ip::tcp::endpoint remote_endpoint = sock.remote_endpoint(err); +++ REQUIRE(!ec); +++ ip::tcp::endpoint local_endpoint = sock.local_endpoint(err); +++ REQUIRE(!ec); +++ +++ std::printf("[%4d] made outgoing connection to: %s:%d. local endpoint: %s:%d\n" +++ , millis +++ , remote_endpoint.address().to_string().c_str(), remote_endpoint.port() +++ , local_endpoint.address().to_string().c_str(), local_endpoint.port()); +++ +++ CHECK(remote_endpoint.port() == 1337); +++ CHECK(remote_endpoint.address().to_string() == "40.30.20.10"); +++ CHECK(local_endpoint.port() != 0); +++ CHECK(local_endpoint.address().to_string() == "10.20.30.40"); +++ +++ std::printf("sending %d bytes\n", int(sizeof(send_buffer))); +++ sock.async_write_some(sim::asio::buffer(send_buffer, sizeof(send_buffer)) +++ , std::bind(&on_sent, _1, _2, std::ref(sock))); +++} +++ +++} +++ +++TEST_CASE("accept incoming connection on acceptor socket", "[acceptor]") +++{ +++ default_config cfg; +++ simulation sim(cfg); +++ io_context incoming_ios(sim, ip::make_address_v4("40.30.20.10")); +++ io_context outgoing_ios(sim, ip::make_address_v4("10.20.30.40")); +++ ip::tcp::acceptor listener(incoming_ios); +++ +++ int millis = int(duration_cast(high_resolution_clock::now() +++ .time_since_epoch()).count()); +++ +++ boost::system::error_code ec; +++ listener.open(ip::tcp::v4(), ec); +++ REQUIRE(!ec); +++ listener.bind(ip::tcp::endpoint(ip::address(), 1337), ec); +++ REQUIRE(!ec); +++ listener.listen(10, ec); +++ REQUIRE(!ec); +++ +++ ip::tcp::socket incoming(incoming_ios); +++ ip::tcp::endpoint remote_endpoint; +++ listener.async_accept(incoming, remote_endpoint +++ , std::bind(&incoming_connection, _1, std::ref(incoming) +++ , std::cref(remote_endpoint))); +++ +++ dump_network_graph(sim, "accept.dot"); +++ +++ std::printf("[%4d] connecting\n", millis); +++ ip::tcp::socket outgoing(outgoing_ios); +++ outgoing.open(ip::tcp::v4(), ec); +++ REQUIRE(!ec); +++ outgoing.async_connect(ip::tcp::endpoint(ip::make_address("40.30.20.10") +++ , 1337), std::bind(&on_connected, _1, std::ref(outgoing))); +++ +++ sim.run(); +++ +++ millis = int(duration_cast(high_resolution_clock::now() +++ .time_since_epoch()).count()); +++ +++ CHECK(num_received == num_sent); +++ CHECK(num_received > 0); +++} +++ ++--- /dev/null +++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/test/catch.hpp ++@@ -0,0 +1,14057 @@ +++ +++// Copyright Catch2 Authors +++// Distributed under the Boost Software License, Version 1.0. +++// (See accompanying file LICENSE.txt or copy at +++// https://www.boost.org/LICENSE_1_0.txt) +++ +++// SPDX-License-Identifier: BSL-1.0 +++ +++// Catch v3.6.0 +++// Generated: 2024-05-05 20:53:27.071502 +++// ---------------------------------------------------------- +++// This file is an amalgamation of multiple different files. +++// You probably shouldn't edit it directly. +++// ---------------------------------------------------------- +++#ifndef CATCH_AMALGAMATED_HPP_INCLUDED +++#define CATCH_AMALGAMATED_HPP_INCLUDED +++ +++ +++/** \file +++ * This is a convenience header for Catch2. It includes **all** of Catch2 headers. +++ * +++ * Generally the Catch2 users should use specific includes they need, +++ * but this header can be used instead for ease-of-experimentation, or +++ * just plain convenience, at the cost of (significantly) increased +++ * compilation times. +++ * +++ * When a new header is added to either the top level folder, or to the +++ * corresponding internal subfolder, it should be added here. Headers +++ * added to the various subparts (e.g. matchers, generators, etc...), +++ * should go their respective catch-all headers. +++ */ +++ +++#ifndef CATCH_ALL_HPP_INCLUDED +++#define CATCH_ALL_HPP_INCLUDED +++ +++ +++ +++/** \file +++ * This is a convenience header for Catch2's benchmarking. It includes +++ * **all** of Catch2 headers related to benchmarking. +++ * +++ * Generally the Catch2 users should use specific includes they need, +++ * but this header can be used instead for ease-of-experimentation, or +++ * just plain convenience, at the cost of (significantly) increased +++ * compilation times. +++ * +++ * When a new header is added to either the `benchmark` folder, or to +++ * the corresponding internal (detail) subfolder, it should be added here. +++ */ +++ +++#ifndef CATCH_BENCHMARK_ALL_HPP_INCLUDED +++#define CATCH_BENCHMARK_ALL_HPP_INCLUDED +++ +++ +++ +++// Adapted from donated nonius code. +++ +++#ifndef CATCH_BENCHMARK_HPP_INCLUDED +++#define CATCH_BENCHMARK_HPP_INCLUDED +++ +++ +++ +++#ifndef CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED +++#define CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED +++ +++// Detect a number of compiler features - by compiler +++// The following features are defined: +++// +++// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? +++// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? +++// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? +++// **************** +++// Note to maintainers: if new toggles are added please document them +++// in configuration.md, too +++// **************** +++ +++// In general each macro has a _NO_ form +++// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. +++// Many features, at point of detection, define an _INTERNAL_ macro, so they +++// can be combined, en-mass, with the _NO_ forms later. +++ +++ +++ +++#ifndef CATCH_PLATFORM_HPP_INCLUDED +++#define CATCH_PLATFORM_HPP_INCLUDED +++ +++// See e.g.: +++// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html +++#ifdef __APPLE__ +++# ifndef __has_extension +++# define __has_extension(x) 0 +++# endif +++# include +++# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ +++ (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) +++# define CATCH_PLATFORM_MAC +++# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) +++# define CATCH_PLATFORM_IPHONE +++# endif +++ +++#elif defined(linux) || defined(__linux) || defined(__linux__) +++# define CATCH_PLATFORM_LINUX +++ +++#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) +++# define CATCH_PLATFORM_WINDOWS +++ +++# if defined( WINAPI_FAMILY ) && ( WINAPI_FAMILY == WINAPI_FAMILY_APP ) +++# define CATCH_PLATFORM_WINDOWS_UWP +++# endif +++ +++#elif defined(__ORBIS__) || defined(__PROSPERO__) +++# define CATCH_PLATFORM_PLAYSTATION +++ +++#endif +++ +++#endif // CATCH_PLATFORM_HPP_INCLUDED +++ +++#ifdef __cplusplus +++ +++# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +++# define CATCH_CPP17_OR_GREATER +++# endif +++ +++# if (__cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) +++# define CATCH_CPP20_OR_GREATER +++# endif +++ +++#endif +++ +++// Only GCC compiler should be used in this block, so other compilers trying to +++// mask themselves as GCC should be ignored. +++#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) && !defined(__NVCOMPILER) +++# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) +++# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) +++ +++// This only works on GCC 9+. so we have to also add a global suppression of Wparentheses +++// for older versions of GCC. +++# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ +++ _Pragma( "GCC diagnostic ignored \"-Wparentheses\"" ) +++ +++# define CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ +++ _Pragma( "GCC diagnostic ignored \"-Wunused-result\"" ) +++ +++# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ +++ _Pragma( "GCC diagnostic ignored \"-Wunused-variable\"" ) +++ +++# define CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ +++ _Pragma( "GCC diagnostic ignored \"-Wuseless-cast\"" ) +++ +++# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ +++ _Pragma( "GCC diagnostic ignored \"-Wshadow\"" ) +++ +++# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) +++ +++#endif +++ +++#if defined(__NVCOMPILER) +++# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "diag push" ) +++# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "diag pop" ) +++# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS _Pragma( "diag_suppress declared_but_not_referenced" ) +++#endif +++ +++#if defined(__CUDACC__) && !defined(__clang__) +++# ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ +++// New pragmas introduced in CUDA 11.5+ +++# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "nv_diagnostic push" ) +++# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "nv_diagnostic pop" ) +++# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS _Pragma( "nv_diag_suppress 177" ) +++# else +++# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS _Pragma( "diag_suppress 177" ) +++# endif +++#endif +++ +++// clang-cl defines _MSC_VER as well as __clang__, which could cause the +++// start/stop internal suppression macros to be double defined. +++#if defined(__clang__) && !defined(_MSC_VER) +++ +++# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) +++# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) +++ +++#endif // __clang__ && !_MSC_VER +++ +++#if defined(__clang__) +++ +++// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug +++// which results in calls to destructors being emitted for each temporary, +++// without a matching initialization. In practice, this can result in something +++// like `std::string::~string` being called on an uninitialized value. +++// +++// For example, this code will likely segfault under IBM XL: +++// ``` +++// REQUIRE(std::string("12") + "34" == "1234") +++// ``` +++// +++// Similarly, NVHPC's implementation of `__builtin_constant_p` has a bug which +++// results in calls to the immediately evaluated lambda expressions to be +++// reported as unevaluated lambdas. +++// https://developer.nvidia.com/nvidia_bug/3321845. +++// +++// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. +++# if !defined(__ibmxl__) && !defined(__CUDACC__) && !defined( __NVCOMPILER ) +++# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ +++# endif +++ +++ +++# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ +++ _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ +++ _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") +++ +++# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ +++ _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) +++ +++# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ +++ _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) +++ +++# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ +++ _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) +++ +++# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ +++ _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) +++ +++# define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS \ +++ _Pragma( "clang diagnostic ignored \"-Wcomma\"" ) +++ +++# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ +++ _Pragma( "clang diagnostic ignored \"-Wshadow\"" ) +++ +++#endif // __clang__ +++ +++ +++//////////////////////////////////////////////////////////////////////////////// +++// We know some environments not to support full POSIX signals +++#if defined( CATCH_PLATFORM_WINDOWS ) || \ +++ defined( CATCH_PLATFORM_PLAYSTATION ) || \ +++ defined( __CYGWIN__ ) || \ +++ defined( __QNX__ ) || \ +++ defined( __EMSCRIPTEN__ ) || \ +++ defined( __DJGPP__ ) || \ +++ defined( __OS400__ ) +++# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +++#else +++# define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS +++#endif +++ +++//////////////////////////////////////////////////////////////////////////////// +++// Assume that some platforms do not support getenv. +++#if defined( CATCH_PLATFORM_WINDOWS_UWP ) || \ +++ defined( CATCH_PLATFORM_PLAYSTATION ) || \ +++ defined( _GAMING_XBOX ) +++# define CATCH_INTERNAL_CONFIG_NO_GETENV +++#else +++# define CATCH_INTERNAL_CONFIG_GETENV +++#endif +++ +++//////////////////////////////////////////////////////////////////////////////// +++// Android somehow still does not support std::to_string +++#if defined(__ANDROID__) +++# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +++#endif +++ +++//////////////////////////////////////////////////////////////////////////////// +++// Not all Windows environments support SEH properly +++#if defined(__MINGW32__) +++# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +++#endif +++ +++//////////////////////////////////////////////////////////////////////////////// +++// PS4 +++#if defined(__ORBIS__) +++# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE +++#endif +++ +++//////////////////////////////////////////////////////////////////////////////// +++// Cygwin +++#ifdef __CYGWIN__ +++ +++// Required for some versions of Cygwin to declare gettimeofday +++// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin +++# define _BSD_SOURCE +++// some versions of cygwin (most) do not support std::to_string. Use the libstd check. +++// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 +++# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ +++ && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) +++ +++# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +++ +++# endif +++#endif // __CYGWIN__ +++ +++//////////////////////////////////////////////////////////////////////////////// +++// Visual C++ +++#if defined(_MSC_VER) +++ +++// We want to defer to nvcc-specific warning suppression if we are compiled +++// with nvcc masquerading for MSVC. +++# if !defined( __CUDACC__ ) +++# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ +++ __pragma( warning( push ) ) +++# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ +++ __pragma( warning( pop ) ) +++# endif +++ +++// Universal Windows platform does not support SEH +++// Or console colours (or console at all...) +++# if defined(CATCH_PLATFORM_WINDOWS_UWP) +++# define CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32 +++# else +++# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +++# endif +++ +++// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ +++// _MSVC_TRADITIONAL == 0 means new conformant preprocessor +++// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor +++# if !defined(__clang__) // Handle Clang masquerading for msvc +++# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +++# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +++# endif // MSVC_TRADITIONAL +++# endif // __clang__ +++ +++#endif // _MSC_VER +++ +++#if defined(_REENTRANT) || defined(_MSC_VER) +++// Enable async processing, as -pthread is specified or no additional linking is required +++# define CATCH_INTERNAL_CONFIG_USE_ASYNC +++#endif // _MSC_VER +++ +++//////////////////////////////////////////////////////////////////////////////// +++// Check if we are compiled with -fno-exceptions or equivalent +++#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) +++# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED +++#endif +++ +++ +++//////////////////////////////////////////////////////////////////////////////// +++// Embarcadero C++Build +++#if defined(__BORLANDC__) +++ #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN +++#endif +++ +++//////////////////////////////////////////////////////////////////////////////// +++ +++// RTX is a special version of Windows that is real time. +++// This means that it is detected as Windows, but does not provide +++// the same set of capabilities as real Windows does. +++#if defined(UNDER_RTSS) || defined(RTX64_BUILD) +++ #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +++ #define CATCH_INTERNAL_CONFIG_NO_ASYNC +++ #define CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32 +++#endif +++ +++#if !defined(_GLIBCXX_USE_C99_MATH_TR1) +++#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER +++#endif +++ +++// Various stdlib support checks that require __has_include +++#if defined(__has_include) +++ // Check if string_view is available and usable +++ #if __has_include() && defined(CATCH_CPP17_OR_GREATER) +++ # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW +++ #endif +++ +++ // Check if optional is available and usable +++ # if __has_include() && defined(CATCH_CPP17_OR_GREATER) +++ # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL +++ # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +++ +++ // Check if byte is available and usable +++ # if __has_include() && defined(CATCH_CPP17_OR_GREATER) +++ # include +++ # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0) +++ # define CATCH_INTERNAL_CONFIG_CPP17_BYTE +++ # endif +++ # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +++ +++ // Check if variant is available and usable +++ # if __has_include() && defined(CATCH_CPP17_OR_GREATER) +++ # if defined(__clang__) && (__clang_major__ < 8) +++ // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 +++ // fix should be in clang 8, workaround in libstdc++ 8.2 +++ # include +++ # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) +++ # define CATCH_CONFIG_NO_CPP17_VARIANT +++ # else +++ # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT +++ # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) +++ # else +++ # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT +++ # endif // defined(__clang__) && (__clang_major__ < 8) +++ # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +++#endif // defined(__has_include) +++ +++ +++#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) +++# define CATCH_CONFIG_WINDOWS_SEH +++#endif +++// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. +++#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +++# define CATCH_CONFIG_POSIX_SIGNALS +++#endif +++ +++#if defined(CATCH_INTERNAL_CONFIG_GETENV) && !defined(CATCH_INTERNAL_CONFIG_NO_GETENV) && !defined(CATCH_CONFIG_NO_GETENV) && !defined(CATCH_CONFIG_GETENV) +++# define CATCH_CONFIG_GETENV +++#endif +++ +++#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) +++# define CATCH_CONFIG_CPP11_TO_STRING +++#endif +++ +++#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) +++# define CATCH_CONFIG_CPP17_OPTIONAL +++#endif +++ +++#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) +++# define CATCH_CONFIG_CPP17_STRING_VIEW +++#endif +++ +++#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) +++# define CATCH_CONFIG_CPP17_VARIANT +++#endif +++ +++#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) +++# define CATCH_CONFIG_CPP17_BYTE +++#endif +++ +++ +++#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) +++# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE +++#endif +++ +++#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) +++# define CATCH_CONFIG_NEW_CAPTURE +++#endif +++ +++#if !defined( CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED ) && \ +++ !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS ) && \ +++ !defined( CATCH_CONFIG_NO_DISABLE_EXCEPTIONS ) +++# define CATCH_CONFIG_DISABLE_EXCEPTIONS +++#endif +++ +++#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) +++# define CATCH_CONFIG_POLYFILL_ISNAN +++#endif +++ +++#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) +++# define CATCH_CONFIG_USE_ASYNC +++#endif +++ +++#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) +++# define CATCH_CONFIG_GLOBAL_NEXTAFTER +++#endif +++ +++ +++// Even if we do not think the compiler has that warning, we still have +++// to provide a macro that can be used by the code. +++#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) +++# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION +++#endif +++#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) +++# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +++#endif +++#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) +++# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +++#endif +++#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) +++# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +++#endif +++#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT) +++# define CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT +++#endif +++#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS) +++# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS +++#endif +++#if !defined(CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS) +++# define CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS +++#endif +++#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) +++# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS +++#endif +++#if !defined( CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS ) +++# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +++#endif +++#if !defined( CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS ) +++# define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS +++#endif +++#if !defined( CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS ) +++# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS +++#endif +++ +++ +++// The goal of this macro is to avoid evaluation of the arguments, but +++// still have the compiler warn on problems inside... +++#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) +++# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) +++#endif +++ +++#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) +++# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +++#elif defined(__clang__) && (__clang_major__ < 5) +++# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +++#endif +++ +++ +++#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +++#define CATCH_TRY if ((true)) +++#define CATCH_CATCH_ALL if ((false)) +++#define CATCH_CATCH_ANON(type) if ((false)) +++#else +++#define CATCH_TRY try +++#define CATCH_CATCH_ALL catch (...) +++#define CATCH_CATCH_ANON(type) catch (type) +++#endif +++ +++#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) +++#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +++#endif +++ +++#if defined( CATCH_PLATFORM_WINDOWS ) && \ +++ !defined( CATCH_CONFIG_COLOUR_WIN32 ) && \ +++ !defined( CATCH_CONFIG_NO_COLOUR_WIN32 ) && \ +++ !defined( CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32 ) +++# define CATCH_CONFIG_COLOUR_WIN32 +++#endif +++ +++#if defined( CATCH_CONFIG_SHARED_LIBRARY ) && defined( _MSC_VER ) && \ +++ !defined( CATCH_CONFIG_STATIC ) +++# ifdef Catch2_EXPORTS +++# define CATCH_EXPORT //__declspec( dllexport ) // not needed +++# else +++# define CATCH_EXPORT __declspec( dllimport ) +++# endif +++#else +++# define CATCH_EXPORT +++#endif +++ +++#endif // CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED +++ +++ +++#ifndef CATCH_CONTEXT_HPP_INCLUDED +++#define CATCH_CONTEXT_HPP_INCLUDED +++ +++ +++namespace Catch { +++ +++ class IResultCapture; +++ class IConfig; +++ +++ class Context { +++ IConfig const* m_config = nullptr; +++ IResultCapture* m_resultCapture = nullptr; +++ +++ CATCH_EXPORT static Context* currentContext; +++ friend Context& getCurrentMutableContext(); +++ friend Context const& getCurrentContext(); +++ static void createContext(); +++ friend void cleanUpContext(); +++ +++ public: +++ IResultCapture* getResultCapture() const { return m_resultCapture; } +++ IConfig const* getConfig() const { return m_config; } +++ void setResultCapture( IResultCapture* resultCapture ); +++ void setConfig( IConfig const* config ); +++ }; +++ +++ Context& getCurrentMutableContext(); +++ +++ inline Context const& getCurrentContext() { +++ // We duplicate the logic from `getCurrentMutableContext` here, +++ // to avoid paying the call overhead in debug mode. +++ if ( !Context::currentContext ) { Context::createContext(); } +++ // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn) +++ return *Context::currentContext; +++ } +++ +++ void cleanUpContext(); +++ +++ class SimplePcg32; +++ SimplePcg32& sharedRng(); +++} +++ +++#endif // CATCH_CONTEXT_HPP_INCLUDED +++ +++ +++#ifndef CATCH_MOVE_AND_FORWARD_HPP_INCLUDED +++#define CATCH_MOVE_AND_FORWARD_HPP_INCLUDED +++ +++#include +++ +++//! Replacement for std::move with better compile time performance +++#define CATCH_MOVE(...) static_cast&&>(__VA_ARGS__) +++ +++//! Replacement for std::forward with better compile time performance +++#define CATCH_FORWARD(...) static_cast(__VA_ARGS__) +++ +++#endif // CATCH_MOVE_AND_FORWARD_HPP_INCLUDED +++ +++ +++#ifndef CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED +++#define CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED +++ +++namespace Catch { +++ +++ //! Used to signal that an assertion macro failed +++ struct TestFailureException{}; +++ //! Used to signal that the remainder of a test should be skipped +++ struct TestSkipException {}; +++ +++ /** +++ * Outlines throwing of `TestFailureException` into a single TU +++ * +++ * Also handles `CATCH_CONFIG_DISABLE_EXCEPTIONS` for callers. +++ */ +++ [[noreturn]] void throw_test_failure_exception(); +++ +++ /** +++ * Outlines throwing of `TestSkipException` into a single TU +++ * +++ * Also handles `CATCH_CONFIG_DISABLE_EXCEPTIONS` for callers. +++ */ +++ [[noreturn]] void throw_test_skip_exception(); +++ +++} // namespace Catch +++ +++#endif // CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED +++ +++ +++#ifndef CATCH_UNIQUE_NAME_HPP_INCLUDED +++#define CATCH_UNIQUE_NAME_HPP_INCLUDED +++ +++ +++ +++ +++/** \file +++ * Wrapper for the CONFIG configuration option +++ * +++ * When generating internal unique names, there are two options. Either +++ * we mix in the current line number, or mix in an incrementing number. +++ * We prefer the latter, using `__COUNTER__`, but users might want to +++ * use the former. +++ */ +++ +++#ifndef CATCH_CONFIG_COUNTER_HPP_INCLUDED +++#define CATCH_CONFIG_COUNTER_HPP_INCLUDED +++ +++ +++#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) +++ #define CATCH_INTERNAL_CONFIG_COUNTER +++#endif +++ +++#if defined( CATCH_INTERNAL_CONFIG_COUNTER ) && \ +++ !defined( CATCH_CONFIG_NO_COUNTER ) && \ +++ !defined( CATCH_CONFIG_COUNTER ) +++# define CATCH_CONFIG_COUNTER +++#endif +++ +++ +++#endif // CATCH_CONFIG_COUNTER_HPP_INCLUDED +++#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +++#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +++#ifdef CATCH_CONFIG_COUNTER +++# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +++#else +++# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +++#endif +++ +++#endif // CATCH_UNIQUE_NAME_HPP_INCLUDED +++ +++ +++#ifndef CATCH_INTERFACES_CAPTURE_HPP_INCLUDED +++#define CATCH_INTERFACES_CAPTURE_HPP_INCLUDED +++ +++#include +++#include +++ +++ +++ +++#ifndef CATCH_STRINGREF_HPP_INCLUDED +++#define CATCH_STRINGREF_HPP_INCLUDED +++ +++#include +++#include +++#include +++#include +++ +++#include +++ +++namespace Catch { +++ +++ /// A non-owning string class (similar to the forthcoming std::string_view) +++ /// Note that, because a StringRef may be a substring of another string, +++ /// it may not be null terminated. +++ class StringRef { +++ public: +++ using size_type = std::size_t; +++ using const_iterator = const char*; +++ +++ static constexpr size_type npos{ static_cast( -1 ) }; +++ +++ private: +++ static constexpr char const* const s_empty = ""; +++ +++ char const* m_start = s_empty; +++ size_type m_size = 0; +++ +++ public: // construction +++ constexpr StringRef() noexcept = default; +++ +++ StringRef( char const* rawChars ) noexcept; +++ +++ constexpr StringRef( char const* rawChars, size_type size ) noexcept +++ : m_start( rawChars ), +++ m_size( size ) +++ {} +++ +++ StringRef( std::string const& stdString ) noexcept +++ : m_start( stdString.c_str() ), +++ m_size( stdString.size() ) +++ {} +++ +++ explicit operator std::string() const { +++ return std::string(m_start, m_size); +++ } +++ +++ public: // operators +++ auto operator == ( StringRef other ) const noexcept -> bool { +++ return m_size == other.m_size +++ && (std::memcmp( m_start, other.m_start, m_size ) == 0); +++ } +++ auto operator != (StringRef other) const noexcept -> bool { +++ return !(*this == other); +++ } +++ +++ constexpr auto operator[] ( size_type index ) const noexcept -> char { +++ assert(index < m_size); +++ return m_start[index]; +++ } +++ +++ bool operator<(StringRef rhs) const noexcept; +++ +++ public: // named queries +++ constexpr auto empty() const noexcept -> bool { +++ return m_size == 0; +++ } +++ constexpr auto size() const noexcept -> size_type { +++ return m_size; +++ } +++ +++ // Returns a substring of [start, start + length). +++ // If start + length > size(), then the substring is [start, size()). +++ // If start > size(), then the substring is empty. +++ constexpr StringRef substr(size_type start, size_type length) const noexcept { +++ if (start < m_size) { +++ const auto shortened_size = m_size - start; +++ return StringRef(m_start + start, (shortened_size < length) ? shortened_size : length); +++ } else { +++ return StringRef(); +++ } +++ } +++ +++ // Returns the current start pointer. May not be null-terminated. +++ constexpr char const* data() const noexcept { +++ return m_start; +++ } +++ +++ constexpr const_iterator begin() const { return m_start; } +++ constexpr const_iterator end() const { return m_start + m_size; } +++ +++ +++ friend std::string& operator += (std::string& lhs, StringRef rhs); +++ friend std::ostream& operator << (std::ostream& os, StringRef str); +++ friend std::string operator+(StringRef lhs, StringRef rhs); +++ +++ /** +++ * Provides a three-way comparison with rhs +++ * +++ * Returns negative number if lhs < rhs, 0 if lhs == rhs, and a positive +++ * number if lhs > rhs +++ */ +++ int compare( StringRef rhs ) const; +++ }; +++ +++ +++ constexpr auto operator ""_sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { +++ return StringRef( rawChars, size ); +++ } +++} // namespace Catch +++ +++constexpr auto operator ""_catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { +++ return Catch::StringRef( rawChars, size ); +++} +++ +++#endif // CATCH_STRINGREF_HPP_INCLUDED +++ +++ +++#ifndef CATCH_RESULT_TYPE_HPP_INCLUDED +++#define CATCH_RESULT_TYPE_HPP_INCLUDED +++ +++namespace Catch { +++ +++ // ResultWas::OfType enum +++ struct ResultWas { enum OfType { +++ Unknown = -1, +++ Ok = 0, +++ Info = 1, +++ Warning = 2, +++ // TODO: Should explicit skip be considered "not OK" (cf. isOk)? I.e., should it have the failure bit? +++ ExplicitSkip = 4, +++ +++ FailureBit = 0x10, +++ +++ ExpressionFailed = FailureBit | 1, +++ ExplicitFailure = FailureBit | 2, +++ +++ Exception = 0x100 | FailureBit, +++ +++ ThrewException = Exception | 1, +++ DidntThrowException = Exception | 2, +++ +++ FatalErrorCondition = 0x200 | FailureBit +++ +++ }; }; +++ +++ bool isOk( ResultWas::OfType resultType ); +++ bool isJustInfo( int flags ); +++ +++ +++ // ResultDisposition::Flags enum +++ struct ResultDisposition { enum Flags { +++ Normal = 0x01, +++ +++ ContinueOnFailure = 0x02, // Failures fail test, but execution continues +++ FalseTest = 0x04, // Prefix expression with ! +++ SuppressFail = 0x08 // Failures are reported but do not fail the test +++ }; }; +++ +++ ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ); +++ +++ bool shouldContinueOnFailure( int flags ); +++ inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } +++ bool shouldSuppressFailure( int flags ); +++ +++} // end namespace Catch +++ +++#endif // CATCH_RESULT_TYPE_HPP_INCLUDED +++ +++ +++#ifndef CATCH_UNIQUE_PTR_HPP_INCLUDED +++#define CATCH_UNIQUE_PTR_HPP_INCLUDED +++ +++#include +++#include +++ +++ +++namespace Catch { +++namespace Detail { +++ /** +++ * A reimplementation of `std::unique_ptr` for improved compilation performance +++ * +++ * Does not support arrays nor custom deleters. +++ */ +++ template +++ class unique_ptr { +++ T* m_ptr; +++ public: +++ constexpr unique_ptr(std::nullptr_t = nullptr): +++ m_ptr{} +++ {} +++ explicit constexpr unique_ptr(T* ptr): +++ m_ptr(ptr) +++ {} +++ +++ template ::value>> +++ unique_ptr(unique_ptr&& from): +++ m_ptr(from.release()) +++ {} +++ +++ template ::value>> +++ unique_ptr& operator=(unique_ptr&& from) { +++ reset(from.release()); +++ +++ return *this; +++ } +++ +++ unique_ptr(unique_ptr const&) = delete; +++ unique_ptr& operator=(unique_ptr const&) = delete; +++ +++ unique_ptr(unique_ptr&& rhs) noexcept: +++ m_ptr(rhs.m_ptr) { +++ rhs.m_ptr = nullptr; +++ } +++ unique_ptr& operator=(unique_ptr&& rhs) noexcept { +++ reset(rhs.release()); +++ +++ return *this; +++ } +++ +++ ~unique_ptr() { +++ delete m_ptr; +++ } +++ +++ T& operator*() { +++ assert(m_ptr); +++ return *m_ptr; +++ } +++ T const& operator*() const { +++ assert(m_ptr); +++ return *m_ptr; +++ } +++ T* operator->() noexcept { +++ assert(m_ptr); +++ return m_ptr; +++ } +++ T const* operator->() const noexcept { +++ assert(m_ptr); +++ return m_ptr; +++ } +++ +++ T* get() { return m_ptr; } +++ T const* get() const { return m_ptr; } +++ +++ void reset(T* ptr = nullptr) { +++ delete m_ptr; +++ m_ptr = ptr; +++ } +++ +++ T* release() { +++ auto temp = m_ptr; +++ m_ptr = nullptr; +++ return temp; +++ } +++ +++ explicit operator bool() const { +++ return m_ptr; +++ } +++ +++ friend void swap(unique_ptr& lhs, unique_ptr& rhs) { +++ auto temp = lhs.m_ptr; +++ lhs.m_ptr = rhs.m_ptr; +++ rhs.m_ptr = temp; +++ } +++ }; +++ +++ //! Specialization to cause compile-time error for arrays +++ template +++ class unique_ptr; +++ +++ template +++ unique_ptr make_unique(Args&&... args) { +++ return unique_ptr(new T(CATCH_FORWARD(args)...)); +++ } +++ +++ +++} // end namespace Detail +++} // end namespace Catch +++ +++#endif // CATCH_UNIQUE_PTR_HPP_INCLUDED +++ +++ +++#ifndef CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED +++#define CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED +++ +++ +++ +++// Adapted from donated nonius code. +++ +++#ifndef CATCH_CLOCK_HPP_INCLUDED +++#define CATCH_CLOCK_HPP_INCLUDED +++ +++#include +++ +++namespace Catch { +++ namespace Benchmark { +++ using IDuration = std::chrono::nanoseconds; +++ using FDuration = std::chrono::duration; +++ +++ template +++ using TimePoint = typename Clock::time_point; +++ +++ using default_clock = std::chrono::steady_clock; +++ } // namespace Benchmark +++} // namespace Catch +++ +++#endif // CATCH_CLOCK_HPP_INCLUDED +++ +++namespace Catch { +++ +++ // We cannot forward declare the type with default template argument +++ // multiple times, so it is split out into a separate header so that +++ // we can prevent multiple declarations in dependees +++ template +++ struct BenchmarkStats; +++ +++} // end namespace Catch +++ +++#endif // CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED +++ +++namespace Catch { +++ +++ class AssertionResult; +++ struct AssertionInfo; +++ struct SectionInfo; +++ struct SectionEndInfo; +++ struct MessageInfo; +++ struct MessageBuilder; +++ struct Counts; +++ struct AssertionReaction; +++ struct SourceLineInfo; +++ +++ class ITransientExpression; +++ class IGeneratorTracker; +++ +++ struct BenchmarkInfo; +++ +++ namespace Generators { +++ class GeneratorUntypedBase; +++ using GeneratorBasePtr = Catch::Detail::unique_ptr; +++ } +++ +++ +++ class IResultCapture { +++ public: +++ virtual ~IResultCapture(); +++ +++ virtual void notifyAssertionStarted( AssertionInfo const& info ) = 0; +++ virtual bool sectionStarted( StringRef sectionName, +++ SourceLineInfo const& sectionLineInfo, +++ Counts& assertions ) = 0; +++ virtual void sectionEnded( SectionEndInfo&& endInfo ) = 0; +++ virtual void sectionEndedEarly( SectionEndInfo&& endInfo ) = 0; +++ +++ virtual IGeneratorTracker* +++ acquireGeneratorTracker( StringRef generatorName, +++ SourceLineInfo const& lineInfo ) = 0; +++ virtual IGeneratorTracker* +++ createGeneratorTracker( StringRef generatorName, +++ SourceLineInfo lineInfo, +++ Generators::GeneratorBasePtr&& generator ) = 0; +++ +++ virtual void benchmarkPreparing( StringRef name ) = 0; +++ virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0; +++ virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0; +++ virtual void benchmarkFailed( StringRef error ) = 0; +++ +++ virtual void pushScopedMessage( MessageInfo const& message ) = 0; +++ virtual void popScopedMessage( MessageInfo const& message ) = 0; +++ +++ virtual void emplaceUnscopedMessage( MessageBuilder&& builder ) = 0; +++ +++ virtual void handleFatalErrorCondition( StringRef message ) = 0; +++ +++ virtual void handleExpr +++ ( AssertionInfo const& info, +++ ITransientExpression const& expr, +++ AssertionReaction& reaction ) = 0; +++ virtual void handleMessage +++ ( AssertionInfo const& info, +++ ResultWas::OfType resultType, +++ StringRef message, +++ AssertionReaction& reaction ) = 0; +++ virtual void handleUnexpectedExceptionNotThrown +++ ( AssertionInfo const& info, +++ AssertionReaction& reaction ) = 0; +++ virtual void handleUnexpectedInflightException +++ ( AssertionInfo const& info, +++ std::string&& message, +++ AssertionReaction& reaction ) = 0; +++ virtual void handleIncomplete +++ ( AssertionInfo const& info ) = 0; +++ virtual void handleNonExpr +++ ( AssertionInfo const &info, +++ ResultWas::OfType resultType, +++ AssertionReaction &reaction ) = 0; +++ +++ +++ +++ virtual bool lastAssertionPassed() = 0; +++ virtual void assertionPassed() = 0; +++ +++ // Deprecated, do not use: +++ virtual std::string getCurrentTestName() const = 0; +++ virtual const AssertionResult* getLastResult() const = 0; +++ virtual void exceptionEarlyReported() = 0; +++ }; +++ +++ IResultCapture& getResultCapture(); +++} +++ +++#endif // CATCH_INTERFACES_CAPTURE_HPP_INCLUDED +++ +++ +++#ifndef CATCH_INTERFACES_CONFIG_HPP_INCLUDED +++#define CATCH_INTERFACES_CONFIG_HPP_INCLUDED +++ +++ +++ +++#ifndef CATCH_NONCOPYABLE_HPP_INCLUDED +++#define CATCH_NONCOPYABLE_HPP_INCLUDED +++ +++namespace Catch { +++ namespace Detail { +++ +++ //! Deriving classes become noncopyable and nonmovable +++ class NonCopyable { +++ NonCopyable( NonCopyable const& ) = delete; +++ NonCopyable( NonCopyable&& ) = delete; +++ NonCopyable& operator=( NonCopyable const& ) = delete; +++ NonCopyable& operator=( NonCopyable&& ) = delete; +++ +++ protected: +++ NonCopyable() noexcept = default; +++ }; +++ +++ } // namespace Detail +++} // namespace Catch +++ +++#endif // CATCH_NONCOPYABLE_HPP_INCLUDED +++ +++#include +++#include +++#include +++#include +++ +++namespace Catch { +++ +++ enum class Verbosity { +++ Quiet = 0, +++ Normal, +++ High +++ }; +++ +++ struct WarnAbout { enum What { +++ Nothing = 0x00, +++ //! A test case or leaf section did not run any assertions +++ NoAssertions = 0x01, +++ //! A command line test spec matched no test cases +++ UnmatchedTestSpec = 0x02, +++ }; }; +++ +++ enum class ShowDurations { +++ DefaultForReporter, +++ Always, +++ Never +++ }; +++ enum class TestRunOrder { +++ Declared, +++ LexicographicallySorted, +++ Randomized +++ }; +++ enum class ColourMode : std::uint8_t { +++ //! Let Catch2 pick implementation based on platform detection +++ PlatformDefault, +++ //! Use ANSI colour code escapes +++ ANSI, +++ //! Use Win32 console colour API +++ Win32, +++ //! Don't use any colour +++ None +++ }; +++ struct WaitForKeypress { enum When { +++ Never, +++ BeforeStart = 1, +++ BeforeExit = 2, +++ BeforeStartAndExit = BeforeStart | BeforeExit +++ }; }; +++ +++ class TestSpec; +++ class IStream; +++ +++ class IConfig : public Detail::NonCopyable { +++ public: +++ virtual ~IConfig(); +++ +++ virtual bool allowThrows() const = 0; +++ virtual StringRef name() const = 0; +++ virtual bool includeSuccessfulResults() const = 0; +++ virtual bool shouldDebugBreak() const = 0; +++ virtual bool warnAboutMissingAssertions() const = 0; +++ virtual bool warnAboutUnmatchedTestSpecs() const = 0; +++ virtual bool zeroTestsCountAsSuccess() const = 0; +++ virtual int abortAfter() const = 0; +++ virtual bool showInvisibles() const = 0; +++ virtual ShowDurations showDurations() const = 0; +++ virtual double minDuration() const = 0; +++ virtual TestSpec const& testSpec() const = 0; +++ virtual bool hasTestFilters() const = 0; +++ virtual std::vector const& getTestsOrTags() const = 0; +++ virtual TestRunOrder runOrder() const = 0; +++ virtual uint32_t rngSeed() const = 0; +++ virtual unsigned int shardCount() const = 0; +++ virtual unsigned int shardIndex() const = 0; +++ virtual ColourMode defaultColourMode() const = 0; +++ virtual std::vector const& getSectionsToRun() const = 0; +++ virtual Verbosity verbosity() const = 0; +++ +++ virtual bool skipBenchmarks() const = 0; +++ virtual bool benchmarkNoAnalysis() const = 0; +++ virtual unsigned int benchmarkSamples() const = 0; +++ virtual double benchmarkConfidenceInterval() const = 0; +++ virtual unsigned int benchmarkResamples() const = 0; +++ virtual std::chrono::milliseconds benchmarkWarmupTime() const = 0; +++ }; +++} +++ +++#endif // CATCH_INTERFACES_CONFIG_HPP_INCLUDED +++ +++ +++#ifndef CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED +++#define CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED +++ +++ +++#include +++ +++namespace Catch { +++ +++ class TestCaseHandle; +++ struct TestCaseInfo; +++ class ITestCaseRegistry; +++ class IExceptionTranslatorRegistry; +++ class IExceptionTranslator; +++ class ReporterRegistry; +++ class IReporterFactory; +++ class ITagAliasRegistry; +++ class ITestInvoker; +++ class IMutableEnumValuesRegistry; +++ struct SourceLineInfo; +++ +++ class StartupExceptionRegistry; +++ class EventListenerFactory; +++ +++ using IReporterFactoryPtr = Detail::unique_ptr; +++ +++ class IRegistryHub { +++ public: +++ virtual ~IRegistryHub(); // = default +++ +++ virtual ReporterRegistry const& getReporterRegistry() const = 0; +++ virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; +++ virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; +++ virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0; +++ +++ +++ virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0; +++ }; +++ +++ class IMutableRegistryHub { +++ public: +++ virtual ~IMutableRegistryHub(); // = default +++ virtual void registerReporter( std::string const& name, IReporterFactoryPtr factory ) = 0; +++ virtual void registerListener( Detail::unique_ptr factory ) = 0; +++ virtual void registerTest(Detail::unique_ptr&& testInfo, Detail::unique_ptr&& invoker) = 0; +++ virtual void registerTranslator( Detail::unique_ptr&& translator ) = 0; +++ virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0; +++ virtual void registerStartupException() noexcept = 0; +++ virtual IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() = 0; +++ }; +++ +++ IRegistryHub const& getRegistryHub(); +++ IMutableRegistryHub& getMutableRegistryHub(); +++ void cleanUp(); +++ std::string translateActiveException(); +++ +++} +++ +++#endif // CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED +++ +++ +++#ifndef CATCH_BENCHMARK_STATS_HPP_INCLUDED +++#define CATCH_BENCHMARK_STATS_HPP_INCLUDED +++ +++ +++ +++// Adapted from donated nonius code. +++ +++#ifndef CATCH_ESTIMATE_HPP_INCLUDED +++#define CATCH_ESTIMATE_HPP_INCLUDED +++ +++namespace Catch { +++ namespace Benchmark { +++ template +++ struct Estimate { +++ Type point; +++ Type lower_bound; +++ Type upper_bound; +++ double confidence_interval; +++ }; +++ } // namespace Benchmark +++} // namespace Catch +++ +++#endif // CATCH_ESTIMATE_HPP_INCLUDED +++ +++ +++// Adapted from donated nonius code. +++ +++#ifndef CATCH_OUTLIER_CLASSIFICATION_HPP_INCLUDED +++#define CATCH_OUTLIER_CLASSIFICATION_HPP_INCLUDED +++ +++namespace Catch { +++ namespace Benchmark { +++ struct OutlierClassification { +++ int samples_seen = 0; +++ int low_severe = 0; // more than 3 times IQR below Q1 +++ int low_mild = 0; // 1.5 to 3 times IQR below Q1 +++ int high_mild = 0; // 1.5 to 3 times IQR above Q3 +++ int high_severe = 0; // more than 3 times IQR above Q3 +++ +++ int total() const { +++ return low_severe + low_mild + high_mild + high_severe; +++ } +++ }; +++ } // namespace Benchmark +++} // namespace Catch +++ +++#endif // CATCH_OUTLIERS_CLASSIFICATION_HPP_INCLUDED +++// The fwd decl & default specialization needs to be seen by VS2017 before +++// BenchmarkStats itself, or VS2017 will report compilation error. +++ +++#include +++#include +++ +++namespace Catch { +++ +++ struct BenchmarkInfo { +++ std::string name; +++ double estimatedDuration; +++ int iterations; +++ unsigned int samples; +++ unsigned int resamples; +++ double clockResolution; +++ double clockCost; +++ }; +++ +++ // We need to keep template parameter for backwards compatibility, +++ // but we also do not want to use the template paraneter. +++ template +++ struct BenchmarkStats { +++ BenchmarkInfo info; +++ +++ std::vector samples; +++ Benchmark::Estimate mean; +++ Benchmark::Estimate standardDeviation; +++ Benchmark::OutlierClassification outliers; +++ double outlierVariance; +++ }; +++ +++ +++} // end namespace Catch +++ +++#endif // CATCH_BENCHMARK_STATS_HPP_INCLUDED +++ +++ +++// Adapted from donated nonius code. +++ +++#ifndef CATCH_ENVIRONMENT_HPP_INCLUDED +++#define CATCH_ENVIRONMENT_HPP_INCLUDED +++ +++ +++namespace Catch { +++ namespace Benchmark { +++ struct EnvironmentEstimate { +++ FDuration mean; +++ OutlierClassification outliers; +++ }; +++ struct Environment { +++ EnvironmentEstimate clock_resolution; +++ EnvironmentEstimate clock_cost; +++ }; +++ } // namespace Benchmark +++} // namespace Catch +++ +++#endif // CATCH_ENVIRONMENT_HPP_INCLUDED +++ +++ +++// Adapted from donated nonius code. +++ +++#ifndef CATCH_EXECUTION_PLAN_HPP_INCLUDED +++#define CATCH_EXECUTION_PLAN_HPP_INCLUDED +++ +++ +++ +++// Adapted from donated nonius code. +++ +++#ifndef CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED +++#define CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED +++ +++ +++ +++// Adapted from donated nonius code. +++ +++#ifndef CATCH_CHRONOMETER_HPP_INCLUDED +++#define CATCH_CHRONOMETER_HPP_INCLUDED +++ +++ +++ +++// Adapted from donated nonius code. +++ +++#ifndef CATCH_OPTIMIZER_HPP_INCLUDED +++#define CATCH_OPTIMIZER_HPP_INCLUDED +++ +++#if defined(_MSC_VER) || defined(__IAR_SYSTEMS_ICC__) +++# include // atomic_thread_fence +++#endif +++ +++ +++#include +++ +++namespace Catch { +++ namespace Benchmark { +++#if defined(__GNUC__) || defined(__clang__) +++ template +++ inline void keep_memory(T* p) { +++ asm volatile("" : : "g"(p) : "memory"); +++ } +++ inline void keep_memory() { +++ asm volatile("" : : : "memory"); +++ } +++ +++ namespace Detail { +++ inline void optimizer_barrier() { keep_memory(); } +++ } // namespace Detail +++#elif defined(_MSC_VER) || defined(__IAR_SYSTEMS_ICC__) +++ +++#if defined(_MSVC_VER) +++#pragma optimize("", off) +++#elif defined(__IAR_SYSTEMS_ICC__) +++// For IAR the pragma only affects the following function +++#pragma optimize=disable +++#endif +++ template +++ inline void keep_memory(T* p) { +++ // thanks @milleniumbug +++ *reinterpret_cast(p) = *reinterpret_cast(p); +++ } +++ // TODO equivalent keep_memory() +++#if defined(_MSVC_VER) +++#pragma optimize("", on) +++#endif +++ +++ namespace Detail { +++ inline void optimizer_barrier() { +++ std::atomic_thread_fence(std::memory_order_seq_cst); +++ } +++ } // namespace Detail +++ +++#endif +++ +++ template +++ inline void deoptimize_value(T&& x) { +++ keep_memory(&x); +++ } +++ +++ template +++ inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t::value> { +++ deoptimize_value(CATCH_FORWARD(fn) (CATCH_FORWARD(args)...)); +++ } +++ +++ template +++ inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t::value> { +++ CATCH_FORWARD((fn)) (CATCH_FORWARD(args)...); +++ } +++ } // namespace Benchmark +++} // namespace Catch +++ +++#endif // CATCH_OPTIMIZER_HPP_INCLUDED +++ +++ +++#ifndef CATCH_META_HPP_INCLUDED +++#define CATCH_META_HPP_INCLUDED +++ +++#include +++ +++namespace Catch { +++ template +++ struct true_given : std::true_type {}; +++ +++ struct is_callable_tester { +++ template +++ static true_given()(std::declval()...))> test(int); +++ template +++ static std::false_type test(...); +++ }; +++ +++ template +++ struct is_callable; +++ +++ template +++ struct is_callable : decltype(is_callable_tester::test(0)) {}; +++ +++ +++#if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703 +++ // std::result_of is deprecated in C++17 and removed in C++20. Hence, it is +++ // replaced with std::invoke_result here. +++ template +++ using FunctionReturnType = std::remove_reference_t>>; +++#else +++ template +++ using FunctionReturnType = std::remove_reference_t>>; +++#endif +++ +++} // namespace Catch +++ +++namespace mpl_{ +++ struct na; +++} +++ +++#endif // CATCH_META_HPP_INCLUDED +++ +++namespace Catch { +++ namespace Benchmark { +++ namespace Detail { +++ struct ChronometerConcept { +++ virtual void start() = 0; +++ virtual void finish() = 0; +++ virtual ~ChronometerConcept(); // = default; +++ +++ ChronometerConcept() = default; +++ ChronometerConcept(ChronometerConcept const&) = default; +++ ChronometerConcept& operator=(ChronometerConcept const&) = default; +++ }; +++ template +++ struct ChronometerModel final : public ChronometerConcept { +++ void start() override { started = Clock::now(); } +++ void finish() override { finished = Clock::now(); } +++ +++ IDuration elapsed() const { +++ return std::chrono::duration_cast( +++ finished - started ); +++ } +++ +++ TimePoint started; +++ TimePoint finished; +++ }; +++ } // namespace Detail +++ +++ struct Chronometer { +++ public: +++ template +++ void measure(Fun&& fun) { measure(CATCH_FORWARD(fun), is_callable()); } +++ +++ int runs() const { return repeats; } +++ +++ Chronometer(Detail::ChronometerConcept& meter, int repeats_) +++ : impl(&meter) +++ , repeats(repeats_) {} +++ +++ private: +++ template +++ void measure(Fun&& fun, std::false_type) { +++ measure([&fun](int) { return fun(); }, std::true_type()); +++ } +++ +++ template +++ void measure(Fun&& fun, std::true_type) { +++ Detail::optimizer_barrier(); +++ impl->start(); +++ for (int i = 0; i < repeats; ++i) invoke_deoptimized(fun, i); +++ impl->finish(); +++ Detail::optimizer_barrier(); +++ } +++ +++ Detail::ChronometerConcept* impl; +++ int repeats; +++ }; +++ } // namespace Benchmark +++} // namespace Catch +++ +++#endif // CATCH_CHRONOMETER_HPP_INCLUDED +++ +++#include +++ +++namespace Catch { +++ namespace Benchmark { +++ namespace Detail { +++ template +++ struct is_related +++ : std::is_same, std::decay_t> {}; +++ +++ /// We need to reinvent std::function because every piece of code that might add overhead +++ /// in a measurement context needs to have consistent performance characteristics so that we +++ /// can account for it in the measurement. +++ /// Implementations of std::function with optimizations that aren't always applicable, like +++ /// small buffer optimizations, are not uncommon. +++ /// This is effectively an implementation of std::function without any such optimizations; +++ /// it may be slow, but it is consistently slow. +++ struct BenchmarkFunction { +++ private: +++ struct callable { +++ virtual void call(Chronometer meter) const = 0; +++ virtual Catch::Detail::unique_ptr clone() const = 0; +++ virtual ~callable(); // = default; +++ +++ callable() = default; +++ callable(callable const&) = default; +++ callable& operator=(callable const&) = default; +++ }; +++ template +++ struct model : public callable { +++ model(Fun&& fun_) : fun(CATCH_MOVE(fun_)) {} +++ model(Fun const& fun_) : fun(fun_) {} +++ +++ Catch::Detail::unique_ptr clone() const override { +++ return Catch::Detail::make_unique>( *this ); +++ } +++ +++ void call(Chronometer meter) const override { +++ call(meter, is_callable()); +++ } +++ void call(Chronometer meter, std::true_type) const { +++ fun(meter); +++ } +++ void call(Chronometer meter, std::false_type) const { +++ meter.measure(fun); +++ } +++ +++ Fun fun; +++ }; +++ +++ struct do_nothing { void operator()() const {} }; +++ +++ template +++ BenchmarkFunction(model* c) : f(c) {} +++ +++ public: +++ BenchmarkFunction() +++ : f(new model{ {} }) {} +++ +++ template ::value, int> = 0> +++ BenchmarkFunction(Fun&& fun) +++ : f(new model>(CATCH_FORWARD(fun))) {} +++ +++ BenchmarkFunction( BenchmarkFunction&& that ) noexcept: +++ f( CATCH_MOVE( that.f ) ) {} +++ +++ BenchmarkFunction(BenchmarkFunction const& that) +++ : f(that.f->clone()) {} +++ +++ BenchmarkFunction& +++ operator=( BenchmarkFunction&& that ) noexcept { +++ f = CATCH_MOVE( that.f ); +++ return *this; +++ } +++ +++ BenchmarkFunction& operator=(BenchmarkFunction const& that) { +++ f = that.f->clone(); +++ return *this; +++ } +++ +++ void operator()(Chronometer meter) const { f->call(meter); } +++ +++ private: +++ Catch::Detail::unique_ptr f; +++ }; +++ } // namespace Detail +++ } // namespace Benchmark +++} // namespace Catch +++ +++#endif // CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED +++ +++ +++// Adapted from donated nonius code. +++ +++#ifndef CATCH_REPEAT_HPP_INCLUDED +++#define CATCH_REPEAT_HPP_INCLUDED +++ +++#include +++ +++namespace Catch { +++ namespace Benchmark { +++ namespace Detail { +++ template +++ struct repeater { +++ void operator()(int k) const { +++ for (int i = 0; i < k; ++i) { +++ fun(); +++ } +++ } +++ Fun fun; +++ }; +++ template +++ repeater> repeat(Fun&& fun) { +++ return { CATCH_FORWARD(fun) }; +++ } +++ } // namespace Detail +++ } // namespace Benchmark +++} // namespace Catch +++ +++#endif // CATCH_REPEAT_HPP_INCLUDED +++ +++ +++// Adapted from donated nonius code. +++ +++#ifndef CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED +++#define CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED +++ +++ +++ +++// Adapted from donated nonius code. +++ +++#ifndef CATCH_MEASURE_HPP_INCLUDED +++#define CATCH_MEASURE_HPP_INCLUDED +++ +++ +++ +++// Adapted from donated nonius code. +++ +++#ifndef CATCH_COMPLETE_INVOKE_HPP_INCLUDED +++#define CATCH_COMPLETE_INVOKE_HPP_INCLUDED +++ +++ +++namespace Catch { +++ namespace Benchmark { +++ namespace Detail { +++ template +++ struct CompleteType { using type = T; }; +++ template <> +++ struct CompleteType { struct type {}; }; +++ +++ template +++ using CompleteType_t = typename CompleteType::type; +++ +++ template +++ struct CompleteInvoker { +++ template +++ static Result invoke(Fun&& fun, Args&&... args) { +++ return CATCH_FORWARD(fun)(CATCH_FORWARD(args)...); +++ } +++ }; +++ template <> +++ struct CompleteInvoker { +++ template +++ static CompleteType_t invoke(Fun&& fun, Args&&... args) { +++ CATCH_FORWARD(fun)(CATCH_FORWARD(args)...); +++ return {}; +++ } +++ }; +++ +++ // invoke and not return void :( +++ template +++ CompleteType_t> complete_invoke(Fun&& fun, Args&&... args) { +++ return CompleteInvoker>::invoke(CATCH_FORWARD(fun), CATCH_FORWARD(args)...); +++ } +++ +++ } // namespace Detail +++ +++ template +++ Detail::CompleteType_t> user_code(Fun&& fun) { +++ return Detail::complete_invoke(CATCH_FORWARD(fun)); +++ } +++ } // namespace Benchmark +++} // namespace Catch +++ +++#endif // CATCH_COMPLETE_INVOKE_HPP_INCLUDED +++ +++ +++// Adapted from donated nonius code. +++ +++#ifndef CATCH_TIMING_HPP_INCLUDED +++#define CATCH_TIMING_HPP_INCLUDED +++ +++ +++#include +++ +++namespace Catch { +++ namespace Benchmark { +++ template +++ struct Timing { +++ IDuration elapsed; +++ Result result; +++ int iterations; +++ }; +++ template +++ using TimingOf = Timing>>; +++ } // namespace Benchmark +++} // namespace Catch +++ +++#endif // CATCH_TIMING_HPP_INCLUDED +++ +++namespace Catch { +++ namespace Benchmark { +++ namespace Detail { +++ template +++ TimingOf measure(Fun&& fun, Args&&... args) { +++ auto start = Clock::now(); +++ auto&& r = Detail::complete_invoke(fun, CATCH_FORWARD(args)...); +++ auto end = Clock::now(); +++ auto delta = end - start; +++ return { delta, CATCH_FORWARD(r), 1 }; +++ } +++ } // namespace Detail +++ } // namespace Benchmark +++} // namespace Catch +++ +++#endif // CATCH_MEASURE_HPP_INCLUDED +++ +++#include +++ +++namespace Catch { +++ namespace Benchmark { +++ namespace Detail { +++ template +++ TimingOf measure_one(Fun&& fun, int iters, std::false_type) { +++ return Detail::measure(fun, iters); +++ } +++ template +++ TimingOf measure_one(Fun&& fun, int iters, std::true_type) { +++ Detail::ChronometerModel meter; +++ auto&& result = Detail::complete_invoke(fun, Chronometer(meter, iters)); +++ +++ return { meter.elapsed(), CATCH_MOVE(result), iters }; +++ } +++ +++ template +++ using run_for_at_least_argument_t = std::conditional_t::value, Chronometer, int>; +++ +++ +++ [[noreturn]] +++ void throw_optimized_away_error(); +++ +++ template +++ TimingOf> +++ run_for_at_least(IDuration how_long, +++ const int initial_iterations, +++ Fun&& fun) { +++ auto iters = initial_iterations; +++ while (iters < (1 << 30)) { +++ auto&& Timing = measure_one(fun, iters, is_callable()); +++ +++ if (Timing.elapsed >= how_long) { +++ return { Timing.elapsed, CATCH_MOVE(Timing.result), iters }; +++ } +++ iters *= 2; +++ } +++ throw_optimized_away_error(); +++ } +++ } // namespace Detail +++ } // namespace Benchmark +++} // namespace Catch +++ +++#endif // CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED +++ +++#include +++ +++namespace Catch { +++ namespace Benchmark { +++ struct ExecutionPlan { +++ int iterations_per_sample; +++ FDuration estimated_duration; +++ Detail::BenchmarkFunction benchmark; +++ FDuration warmup_time; +++ int warmup_iterations; +++ +++ template +++ std::vector run(const IConfig &cfg, Environment env) const { +++ // warmup a bit +++ Detail::run_for_at_least( +++ std::chrono::duration_cast( warmup_time ), +++ warmup_iterations, +++ Detail::repeat( []() { return Clock::now(); } ) +++ ); +++ +++ std::vector times; +++ const auto num_samples = cfg.benchmarkSamples(); +++ times.reserve( num_samples ); +++ for ( size_t i = 0; i < num_samples; ++i ) { +++ Detail::ChronometerModel model; +++ this->benchmark( Chronometer( model, iterations_per_sample ) ); +++ auto sample_time = model.elapsed() - env.clock_cost.mean; +++ if ( sample_time < FDuration::zero() ) { +++ sample_time = FDuration::zero(); +++ } +++ times.push_back(sample_time / iterations_per_sample); +++ } +++ return times; +++ } +++ }; +++ } // namespace Benchmark +++} // namespace Catch +++ +++#endif // CATCH_EXECUTION_PLAN_HPP_INCLUDED +++ +++ +++// Adapted from donated nonius code. +++ +++#ifndef CATCH_ESTIMATE_CLOCK_HPP_INCLUDED +++#define CATCH_ESTIMATE_CLOCK_HPP_INCLUDED +++ +++ +++ +++// Adapted from donated nonius code. +++ +++#ifndef CATCH_STATS_HPP_INCLUDED +++#define CATCH_STATS_HPP_INCLUDED +++ +++ +++#include +++ +++namespace Catch { +++ namespace Benchmark { +++ namespace Detail { +++ using sample = std::vector; +++ +++ double weighted_average_quantile( int k, +++ int q, +++ double* first, +++ double* last ); +++ +++ OutlierClassification +++ classify_outliers( double const* first, double const* last ); +++ +++ double mean( double const* first, double const* last ); +++ +++ double normal_cdf( double x ); +++ +++ double erfc_inv(double x); +++ +++ double normal_quantile(double p); +++ +++ Estimate +++ bootstrap( double confidence_level, +++ double* first, +++ double* last, +++ sample const& resample, +++ double ( *estimator )( double const*, double const* ) ); +++ +++ struct bootstrap_analysis { +++ Estimate mean; +++ Estimate standard_deviation; +++ double outlier_variance; +++ }; +++ +++ bootstrap_analysis analyse_samples(double confidence_level, +++ unsigned int n_resamples, +++ double* first, +++ double* last); +++ } // namespace Detail +++ } // namespace Benchmark +++} // namespace Catch +++ +++#endif // CATCH_STATS_HPP_INCLUDED +++ +++#include +++#include +++#include +++ +++namespace Catch { +++ namespace Benchmark { +++ namespace Detail { +++ template +++ std::vector resolution(int k) { +++ std::vector> times; +++ times.reserve(static_cast(k + 1)); +++ for ( int i = 0; i < k + 1; ++i ) { +++ times.push_back( Clock::now() ); +++ } +++ +++ std::vector deltas; +++ deltas.reserve(static_cast(k)); +++ for ( size_t idx = 1; idx < times.size(); ++idx ) { +++ deltas.push_back( static_cast( +++ ( times[idx] - times[idx - 1] ).count() ) ); +++ } +++ +++ return deltas; +++ } +++ +++ constexpr auto warmup_iterations = 10000; +++ constexpr auto warmup_time = std::chrono::milliseconds(100); +++ constexpr auto minimum_ticks = 1000; +++ constexpr auto warmup_seed = 10000; +++ constexpr auto clock_resolution_estimation_time = std::chrono::milliseconds(500); +++ constexpr auto clock_cost_estimation_time_limit = std::chrono::seconds(1); +++ constexpr auto clock_cost_estimation_tick_limit = 100000; +++ constexpr auto clock_cost_estimation_time = std::chrono::milliseconds(10); +++ constexpr auto clock_cost_estimation_iterations = 10000; +++ +++ template +++ int warmup() { +++ return run_for_at_least(warmup_time, warmup_seed, &resolution) +++ .iterations; +++ } +++ template +++ EnvironmentEstimate estimate_clock_resolution(int iterations) { +++ auto r = run_for_at_least(clock_resolution_estimation_time, iterations, &resolution) +++ .result; +++ return { +++ FDuration(mean(r.data(), r.data() + r.size())), +++ classify_outliers(r.data(), r.data() + r.size()), +++ }; +++ } +++ template +++ EnvironmentEstimate estimate_clock_cost(FDuration resolution) { +++ auto time_limit = (std::min)( +++ resolution * clock_cost_estimation_tick_limit, +++ FDuration(clock_cost_estimation_time_limit)); +++ auto time_clock = [](int k) { +++ return Detail::measure([k] { +++ for (int i = 0; i < k; ++i) { +++ volatile auto ignored = Clock::now(); +++ (void)ignored; +++ } +++ }).elapsed; +++ }; +++ time_clock(1); +++ int iters = clock_cost_estimation_iterations; +++ auto&& r = run_for_at_least(clock_cost_estimation_time, iters, time_clock); +++ std::vector times; +++ int nsamples = static_cast(std::ceil(time_limit / r.elapsed)); +++ times.reserve(static_cast(nsamples)); +++ for ( int s = 0; s < nsamples; ++s ) { +++ times.push_back( static_cast( +++ ( time_clock( r.iterations ) / r.iterations ) +++ .count() ) ); +++ } +++ return { +++ FDuration(mean(times.data(), times.data() + times.size())), +++ classify_outliers(times.data(), times.data() + times.size()), +++ }; +++ } +++ +++ template +++ Environment measure_environment() { +++#if defined(__clang__) +++# pragma clang diagnostic push +++# pragma clang diagnostic ignored "-Wexit-time-destructors" +++#endif +++ static Catch::Detail::unique_ptr env; +++#if defined(__clang__) +++# pragma clang diagnostic pop +++#endif +++ if (env) { +++ return *env; +++ } +++ +++ auto iters = Detail::warmup(); +++ auto resolution = Detail::estimate_clock_resolution(iters); +++ auto cost = Detail::estimate_clock_cost(resolution.mean); +++ +++ env = Catch::Detail::make_unique( Environment{resolution, cost} ); +++ return *env; +++ } +++ } // namespace Detail +++ } // namespace Benchmark +++} // namespace Catch +++ +++#endif // CATCH_ESTIMATE_CLOCK_HPP_INCLUDED +++ +++ +++// Adapted from donated nonius code. +++ +++#ifndef CATCH_ANALYSE_HPP_INCLUDED +++#define CATCH_ANALYSE_HPP_INCLUDED +++ +++ +++ +++// Adapted from donated nonius code. +++ +++#ifndef CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED +++#define CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED +++ +++ +++#include +++ +++namespace Catch { +++ namespace Benchmark { +++ struct SampleAnalysis { +++ std::vector samples; +++ Estimate mean; +++ Estimate standard_deviation; +++ OutlierClassification outliers; +++ double outlier_variance; +++ }; +++ } // namespace Benchmark +++} // namespace Catch +++ +++#endif // CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED +++ +++ +++namespace Catch { +++ class IConfig; +++ +++ namespace Benchmark { +++ namespace Detail { +++ SampleAnalysis analyse(const IConfig &cfg, FDuration* first, FDuration* last); +++ } // namespace Detail +++ } // namespace Benchmark +++} // namespace Catch +++ +++#endif // CATCH_ANALYSE_HPP_INCLUDED +++ +++#include +++#include +++#include +++#include +++#include +++ +++namespace Catch { +++ namespace Benchmark { +++ struct Benchmark { +++ Benchmark(std::string&& benchmarkName) +++ : name(CATCH_MOVE(benchmarkName)) {} +++ +++ template +++ Benchmark(std::string&& benchmarkName , FUN &&func) +++ : fun(CATCH_MOVE(func)), name(CATCH_MOVE(benchmarkName)) {} +++ +++ template +++ ExecutionPlan prepare(const IConfig &cfg, Environment env) const { +++ auto min_time = env.clock_resolution.mean * Detail::minimum_ticks; +++ auto run_time = std::max(min_time, std::chrono::duration_cast(cfg.benchmarkWarmupTime())); +++ auto&& test = Detail::run_for_at_least(std::chrono::duration_cast(run_time), 1, fun); +++ int new_iters = static_cast(std::ceil(min_time * test.iterations / test.elapsed)); +++ return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), fun, std::chrono::duration_cast(cfg.benchmarkWarmupTime()), Detail::warmup_iterations }; +++ } +++ +++ template +++ void run() { +++ static_assert( Clock::is_steady, +++ "Benchmarking clock should be steady" ); +++ auto const* cfg = getCurrentContext().getConfig(); +++ +++ auto env = Detail::measure_environment(); +++ +++ getResultCapture().benchmarkPreparing(name); +++ CATCH_TRY{ +++ auto plan = user_code([&] { +++ return prepare(*cfg, env); +++ }); +++ +++ BenchmarkInfo info { +++ CATCH_MOVE(name), +++ plan.estimated_duration.count(), +++ plan.iterations_per_sample, +++ cfg->benchmarkSamples(), +++ cfg->benchmarkResamples(), +++ env.clock_resolution.mean.count(), +++ env.clock_cost.mean.count() +++ }; +++ +++ getResultCapture().benchmarkStarting(info); +++ +++ auto samples = user_code([&] { +++ return plan.template run(*cfg, env); +++ }); +++ +++ auto analysis = Detail::analyse(*cfg, samples.data(), samples.data() + samples.size()); +++ BenchmarkStats<> stats{ CATCH_MOVE(info), CATCH_MOVE(analysis.samples), analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance }; +++ getResultCapture().benchmarkEnded(stats); +++ } CATCH_CATCH_ANON (TestFailureException const&) { +++ getResultCapture().benchmarkFailed("Benchmark failed due to failed assertion"_sr); +++ } CATCH_CATCH_ALL{ +++ getResultCapture().benchmarkFailed(translateActiveException()); +++ // We let the exception go further up so that the +++ // test case is marked as failed. +++ std::rethrow_exception(std::current_exception()); +++ } +++ } +++ +++ // sets lambda to be used in fun *and* executes benchmark! +++ template ::value, int> = 0> +++ Benchmark & operator=(Fun func) { +++ auto const* cfg = getCurrentContext().getConfig(); +++ if (!cfg->skipBenchmarks()) { +++ fun = Detail::BenchmarkFunction(func); +++ run(); +++ } +++ return *this; +++ } +++ +++ explicit operator bool() { +++ return true; +++ } +++ +++ private: +++ Detail::BenchmarkFunction fun; +++ std::string name; +++ }; +++ } +++} // namespace Catch +++ +++#define INTERNAL_CATCH_GET_1_ARG(arg1, arg2, ...) arg1 +++#define INTERNAL_CATCH_GET_2_ARG(arg1, arg2, ...) arg2 +++ +++#define INTERNAL_CATCH_BENCHMARK(BenchmarkName, name, benchmarkIndex)\ +++ if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \ +++ BenchmarkName = [&](int benchmarkIndex) +++ +++#define INTERNAL_CATCH_BENCHMARK_ADVANCED(BenchmarkName, name)\ +++ if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \ +++ BenchmarkName = [&] +++ +++#if defined(CATCH_CONFIG_PREFIX_ALL) +++ +++#define CATCH_BENCHMARK(...) \ +++ INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,)) +++#define CATCH_BENCHMARK_ADVANCED(name) \ +++ INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), name) +++ +++#else +++ +++#define BENCHMARK(...) \ +++ INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,)) +++#define BENCHMARK_ADVANCED(name) \ +++ INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), name) +++ +++#endif +++ +++#endif // CATCH_BENCHMARK_HPP_INCLUDED +++ +++ +++// Adapted from donated nonius code. +++ +++#ifndef CATCH_CONSTRUCTOR_HPP_INCLUDED +++#define CATCH_CONSTRUCTOR_HPP_INCLUDED +++ +++ +++#include +++ +++namespace Catch { +++ namespace Benchmark { +++ namespace Detail { +++ template +++ struct ObjectStorage +++ { +++ ObjectStorage() = default; +++ +++ ObjectStorage(const ObjectStorage& other) +++ { +++ new(&data) T(other.stored_object()); +++ } +++ +++ ObjectStorage(ObjectStorage&& other) +++ { +++ new(data) T(CATCH_MOVE(other.stored_object())); +++ } +++ +++ ~ObjectStorage() { destruct_on_exit(); } +++ +++ template +++ void construct(Args&&... args) +++ { +++ new (data) T(CATCH_FORWARD(args)...); +++ } +++ +++ template +++ std::enable_if_t destruct() +++ { +++ stored_object().~T(); +++ } +++ +++ private: +++ // If this is a constructor benchmark, destruct the underlying object +++ template +++ void destruct_on_exit(std::enable_if_t* = nullptr) { destruct(); } +++ // Otherwise, don't +++ template +++ void destruct_on_exit(std::enable_if_t* = nullptr) { } +++ +++#if defined( __GNUC__ ) && __GNUC__ <= 6 +++# pragma GCC diagnostic push +++# pragma GCC diagnostic ignored "-Wstrict-aliasing" +++#endif +++ T& stored_object() { return *reinterpret_cast( data ); } +++ +++ T const& stored_object() const { +++ return *reinterpret_cast( data ); +++ } +++#if defined( __GNUC__ ) && __GNUC__ <= 6 +++# pragma GCC diagnostic pop +++#endif +++ +++ alignas( T ) unsigned char data[sizeof( T )]{}; +++ }; +++ } // namespace Detail +++ +++ template +++ using storage_for = Detail::ObjectStorage; +++ +++ template +++ using destructable_object = Detail::ObjectStorage; +++ } // namespace Benchmark +++} // namespace Catch +++ +++#endif // CATCH_CONSTRUCTOR_HPP_INCLUDED +++ +++#endif // CATCH_BENCHMARK_ALL_HPP_INCLUDED +++ +++ +++#ifndef CATCH_APPROX_HPP_INCLUDED +++#define CATCH_APPROX_HPP_INCLUDED +++ +++ +++ +++#ifndef CATCH_TOSTRING_HPP_INCLUDED +++#define CATCH_TOSTRING_HPP_INCLUDED +++ +++ +++#include +++#include +++#include +++#include +++ +++ +++ +++ +++/** \file +++ * Wrapper for the WCHAR configuration option +++ * +++ * We want to support platforms that do not provide `wchar_t`, so we +++ * sometimes have to disable providing wchar_t overloads through Catch2, +++ * e.g. the StringMaker specialization for `std::wstring`. +++ */ +++ +++#ifndef CATCH_CONFIG_WCHAR_HPP_INCLUDED +++#define CATCH_CONFIG_WCHAR_HPP_INCLUDED +++ +++ +++// We assume that WCHAR should be enabled by default, and only disabled +++// for a shortlist (so far only DJGPP) of compilers. +++ +++#if defined(__DJGPP__) +++# define CATCH_INTERNAL_CONFIG_NO_WCHAR +++#endif // __DJGPP__ +++ +++#if !defined( CATCH_INTERNAL_CONFIG_NO_WCHAR ) && \ +++ !defined( CATCH_CONFIG_NO_WCHAR ) && \ +++ !defined( CATCH_CONFIG_WCHAR ) +++# define CATCH_CONFIG_WCHAR +++#endif +++ +++#endif // CATCH_CONFIG_WCHAR_HPP_INCLUDED +++ +++ +++#ifndef CATCH_REUSABLE_STRING_STREAM_HPP_INCLUDED +++#define CATCH_REUSABLE_STRING_STREAM_HPP_INCLUDED +++ +++ +++#include +++#include +++#include +++#include +++ +++namespace Catch { +++ +++ class ReusableStringStream : Detail::NonCopyable { +++ std::size_t m_index; +++ std::ostream* m_oss; +++ public: +++ ReusableStringStream(); +++ ~ReusableStringStream(); +++ +++ //! Returns the serialized state +++ std::string str() const; +++ //! Sets internal state to `str` +++ void str(std::string const& str); +++ +++#if defined(__GNUC__) && !defined(__clang__) +++#pragma GCC diagnostic push +++// Old versions of GCC do not understand -Wnonnull-compare +++#pragma GCC diagnostic ignored "-Wpragmas" +++// Streaming a function pointer triggers Waddress and Wnonnull-compare +++// on GCC, because it implicitly converts it to bool and then decides +++// that the check it uses (a? true : false) is tautological and cannot +++// be null... +++#pragma GCC diagnostic ignored "-Waddress" +++#pragma GCC diagnostic ignored "-Wnonnull-compare" +++#endif +++ +++ template +++ auto operator << ( T const& value ) -> ReusableStringStream& { +++ *m_oss << value; +++ return *this; +++ } +++ +++#if defined(__GNUC__) && !defined(__clang__) +++#pragma GCC diagnostic pop +++#endif +++ auto get() -> std::ostream& { return *m_oss; } +++ }; +++} +++ +++#endif // CATCH_REUSABLE_STRING_STREAM_HPP_INCLUDED +++ +++ +++#ifndef CATCH_VOID_TYPE_HPP_INCLUDED +++#define CATCH_VOID_TYPE_HPP_INCLUDED +++ +++ +++namespace Catch { +++ namespace Detail { +++ +++ template +++ struct make_void { using type = void; }; +++ +++ template +++ using void_t = typename make_void::type; +++ +++ } // namespace Detail +++} // namespace Catch +++ +++ +++#endif // CATCH_VOID_TYPE_HPP_INCLUDED +++ +++ +++#ifndef CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED +++#define CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED +++ +++ +++#include +++ +++namespace Catch { +++ +++ namespace Detail { +++ struct EnumInfo { +++ StringRef m_name; +++ std::vector> m_values; +++ +++ ~EnumInfo(); +++ +++ StringRef lookup( int value ) const; +++ }; +++ } // namespace Detail +++ +++ class IMutableEnumValuesRegistry { +++ public: +++ virtual ~IMutableEnumValuesRegistry(); // = default; +++ +++ virtual Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector const& values ) = 0; +++ +++ template +++ Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::initializer_list values ) { +++ static_assert(sizeof(int) >= sizeof(E), "Cannot serialize enum to int"); +++ std::vector intValues; +++ intValues.reserve( values.size() ); +++ for( auto enumValue : values ) +++ intValues.push_back( static_cast( enumValue ) ); +++ return registerEnum( enumName, allEnums, intValues ); +++ } +++ }; +++ +++} // Catch +++ +++#endif // CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED +++ +++#ifdef CATCH_CONFIG_CPP17_STRING_VIEW +++#include +++#endif +++ +++#ifdef _MSC_VER +++#pragma warning(push) +++#pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless +++#endif +++ +++// We need a dummy global operator<< so we can bring it into Catch namespace later +++struct Catch_global_namespace_dummy{}; +++std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); +++ +++namespace Catch { +++ // Bring in global namespace operator<< for ADL lookup in +++ // `IsStreamInsertable` below. +++ using ::operator<<; +++ +++ namespace Detail { +++ +++ inline std::size_t catch_strnlen(const char *str, std::size_t n) { +++ auto ret = std::char_traits::find(str, n, '\0'); +++ if (ret != nullptr) { +++ return static_cast(ret - str); +++ } +++ return n; +++ } +++ +++ constexpr StringRef unprintableString = "{?}"_sr; +++ +++ //! Encases `string in quotes, and optionally escapes invisibles +++ std::string convertIntoString( StringRef string, bool escapeInvisibles ); +++ +++ //! Encases `string` in quotes, and escapes invisibles if user requested +++ //! it via CLI +++ std::string convertIntoString( StringRef string ); +++ +++ std::string rawMemoryToString( const void *object, std::size_t size ); +++ +++ template +++ std::string rawMemoryToString( const T& object ) { +++ return rawMemoryToString( &object, sizeof(object) ); +++ } +++ +++ template +++ class IsStreamInsertable { +++ template +++ static auto test(int) +++ -> decltype(std::declval() << std::declval(), std::true_type()); +++ +++ template +++ static auto test(...)->std::false_type; +++ +++ public: +++ static const bool value = decltype(test(0))::value; +++ }; +++ +++ template +++ std::string convertUnknownEnumToString( E e ); +++ +++ template +++ std::enable_if_t< +++ !std::is_enum::value && !std::is_base_of::value, +++ std::string> convertUnstreamable( T const& ) { +++ return std::string(Detail::unprintableString); +++ } +++ template +++ std::enable_if_t< +++ !std::is_enum::value && std::is_base_of::value, +++ std::string> convertUnstreamable(T const& ex) { +++ return ex.what(); +++ } +++ +++ +++ template +++ std::enable_if_t< +++ std::is_enum::value, +++ std::string> convertUnstreamable( T const& value ) { +++ return convertUnknownEnumToString( value ); +++ } +++ +++#if defined(_MANAGED) +++ //! Convert a CLR string to a utf8 std::string +++ template +++ std::string clrReferenceToString( T^ ref ) { +++ if (ref == nullptr) +++ return std::string("null"); +++ auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString()); +++ cli::pin_ptr p = &bytes[0]; +++ return std::string(reinterpret_cast(p), bytes->Length); +++ } +++#endif +++ +++ } // namespace Detail +++ +++ +++ template +++ struct StringMaker { +++ template +++ static +++ std::enable_if_t<::Catch::Detail::IsStreamInsertable::value, std::string> +++ convert(const Fake& value) { +++ ReusableStringStream rss; +++ // NB: call using the function-like syntax to avoid ambiguity with +++ // user-defined templated operator<< under clang. +++ rss.operator<<(value); +++ return rss.str(); +++ } +++ +++ template +++ static +++ std::enable_if_t::value, std::string> +++ convert( const Fake& value ) { +++#if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER) +++ return Detail::convertUnstreamable(value); +++#else +++ return CATCH_CONFIG_FALLBACK_STRINGIFIER(value); +++#endif +++ } +++ }; +++ +++ namespace Detail { +++ +++ // This function dispatches all stringification requests inside of Catch. +++ // Should be preferably called fully qualified, like ::Catch::Detail::stringify +++ template +++ std::string stringify(const T& e) { +++ return ::Catch::StringMaker>>::convert(e); +++ } +++ +++ template +++ std::string convertUnknownEnumToString( E e ) { +++ return ::Catch::Detail::stringify(static_cast>(e)); +++ } +++ +++#if defined(_MANAGED) +++ template +++ std::string stringify( T^ e ) { +++ return ::Catch::StringMaker::convert(e); +++ } +++#endif +++ +++ } // namespace Detail +++ +++ // Some predefined specializations +++ +++ template<> +++ struct StringMaker { +++ static std::string convert(const std::string& str); +++ }; +++ +++#ifdef CATCH_CONFIG_CPP17_STRING_VIEW +++ template<> +++ struct StringMaker { +++ static std::string convert(std::string_view str); +++ }; +++#endif +++ +++ template<> +++ struct StringMaker { +++ static std::string convert(char const * str); +++ }; +++ template<> +++ struct StringMaker { +++ static std::string convert(char * str); +++ }; +++ +++#if defined(CATCH_CONFIG_WCHAR) +++ template<> +++ struct StringMaker { +++ static std::string convert(const std::wstring& wstr); +++ }; +++ +++# ifdef CATCH_CONFIG_CPP17_STRING_VIEW +++ template<> +++ struct StringMaker { +++ static std::string convert(std::wstring_view str); +++ }; +++# endif +++ +++ template<> +++ struct StringMaker { +++ static std::string convert(wchar_t const * str); +++ }; +++ template<> +++ struct StringMaker { +++ static std::string convert(wchar_t * str); +++ }; +++#endif // CATCH_CONFIG_WCHAR +++ +++ template +++ struct StringMaker { +++ static std::string convert(char const* str) { +++ return Detail::convertIntoString( +++ StringRef( str, Detail::catch_strnlen( str, SZ ) ) ); +++ } +++ }; +++ template +++ struct StringMaker { +++ static std::string convert(signed char const* str) { +++ auto reinterpreted = reinterpret_cast(str); +++ return Detail::convertIntoString( +++ StringRef(reinterpreted, Detail::catch_strnlen(reinterpreted, SZ))); +++ } +++ }; +++ template +++ struct StringMaker { +++ static std::string convert(unsigned char const* str) { +++ auto reinterpreted = reinterpret_cast(str); +++ return Detail::convertIntoString( +++ StringRef(reinterpreted, Detail::catch_strnlen(reinterpreted, SZ))); +++ } +++ }; +++ +++#if defined(CATCH_CONFIG_CPP17_BYTE) +++ template<> +++ struct StringMaker { +++ static std::string convert(std::byte value); +++ }; +++#endif // defined(CATCH_CONFIG_CPP17_BYTE) +++ template<> +++ struct StringMaker { +++ static std::string convert(int value); +++ }; +++ template<> +++ struct StringMaker { +++ static std::string convert(long value); +++ }; +++ template<> +++ struct StringMaker { +++ static std::string convert(long long value); +++ }; +++ template<> +++ struct StringMaker { +++ static std::string convert(unsigned int value); +++ }; +++ template<> +++ struct StringMaker { +++ static std::string convert(unsigned long value); +++ }; +++ template<> +++ struct StringMaker { +++ static std::string convert(unsigned long long value); +++ }; +++ +++ template<> +++ struct StringMaker { +++ static std::string convert(bool b) { +++ using namespace std::string_literals; +++ return b ? "true"s : "false"s; +++ } +++ }; +++ +++ template<> +++ struct StringMaker { +++ static std::string convert(char c); +++ }; +++ template<> +++ struct StringMaker { +++ static std::string convert(signed char value); +++ }; +++ template<> +++ struct StringMaker { +++ static std::string convert(unsigned char value); +++ }; +++ +++ template<> +++ struct StringMaker { +++ static std::string convert(std::nullptr_t) { +++ using namespace std::string_literals; +++ return "nullptr"s; +++ } +++ }; +++ +++ template<> +++ struct StringMaker { +++ static std::string convert(float value); +++ CATCH_EXPORT static int precision; +++ }; +++ +++ template<> +++ struct StringMaker { +++ static std::string convert(double value); +++ CATCH_EXPORT static int precision; +++ }; +++ +++ template +++ struct StringMaker { +++ template +++ static std::string convert(U* p) { +++ if (p) { +++ return ::Catch::Detail::rawMemoryToString(p); +++ } else { +++ return "nullptr"; +++ } +++ } +++ }; +++ +++ template +++ struct StringMaker { +++ static std::string convert(R C::* p) { +++ if (p) { +++ return ::Catch::Detail::rawMemoryToString(p); +++ } else { +++ return "nullptr"; +++ } +++ } +++ }; +++ +++#if defined(_MANAGED) +++ template +++ struct StringMaker { +++ static std::string convert( T^ ref ) { +++ return ::Catch::Detail::clrReferenceToString(ref); +++ } +++ }; +++#endif +++ +++ namespace Detail { +++ template +++ std::string rangeToString(InputIterator first, Sentinel last) { +++ ReusableStringStream rss; +++ rss << "{ "; +++ if (first != last) { +++ rss << ::Catch::Detail::stringify(*first); +++ for (++first; first != last; ++first) +++ rss << ", " << ::Catch::Detail::stringify(*first); +++ } +++ rss << " }"; +++ return rss.str(); +++ } +++ } +++ +++} // namespace Catch +++ +++////////////////////////////////////////////////////// +++// Separate std-lib types stringification, so it can be selectively enabled +++// This means that we do not bring in their headers +++ +++#if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS) +++# define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER +++# define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER +++# define CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER +++# define CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER +++#endif +++ +++// Separate std::pair specialization +++#if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER) +++#include +++namespace Catch { +++ template +++ struct StringMaker > { +++ static std::string convert(const std::pair& pair) { +++ ReusableStringStream rss; +++ rss << "{ " +++ << ::Catch::Detail::stringify(pair.first) +++ << ", " +++ << ::Catch::Detail::stringify(pair.second) +++ << " }"; +++ return rss.str(); +++ } +++ }; +++} +++#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER +++ +++#if defined(CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_OPTIONAL) +++#include +++namespace Catch { +++ template +++ struct StringMaker > { +++ static std::string convert(const std::optional& optional) { +++ if (optional.has_value()) { +++ return ::Catch::Detail::stringify(*optional); +++ } else { +++ return "{ }"; +++ } +++ } +++ }; +++ template <> +++ struct StringMaker { +++ static std::string convert(const std::nullopt_t&) { +++ return "{ }"; +++ } +++ }; +++} +++#endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER +++ +++// Separate std::tuple specialization +++#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER) +++#include +++namespace Catch { +++ namespace Detail { +++ template< +++ typename Tuple, +++ std::size_t N = 0, +++ bool = (N < std::tuple_size::value) +++ > +++ struct TupleElementPrinter { +++ static void print(const Tuple& tuple, std::ostream& os) { +++ os << (N ? ", " : " ") +++ << ::Catch::Detail::stringify(std::get(tuple)); +++ TupleElementPrinter::print(tuple, os); +++ } +++ }; +++ +++ template< +++ typename Tuple, +++ std::size_t N +++ > +++ struct TupleElementPrinter { +++ static void print(const Tuple&, std::ostream&) {} +++ }; +++ +++ } +++ +++ +++ template +++ struct StringMaker> { +++ static std::string convert(const std::tuple& tuple) { +++ ReusableStringStream rss; +++ rss << '{'; +++ Detail::TupleElementPrinter>::print(tuple, rss.get()); +++ rss << " }"; +++ return rss.str(); +++ } +++ }; +++} +++#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER +++ +++#if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT) +++#include +++namespace Catch { +++ template<> +++ struct StringMaker { +++ static std::string convert(const std::monostate&) { +++ return "{ }"; +++ } +++ }; +++ +++ template +++ struct StringMaker> { +++ static std::string convert(const std::variant& variant) { +++ if (variant.valueless_by_exception()) { +++ return "{valueless variant}"; +++ } else { +++ return std::visit( +++ [](const auto& value) { +++ return ::Catch::Detail::stringify(value); +++ }, +++ variant +++ ); +++ } +++ } +++ }; +++} +++#endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER +++ +++namespace Catch { +++ // Import begin/ end from std here +++ using std::begin; +++ using std::end; +++ +++ namespace Detail { +++ template +++ struct is_range_impl : std::false_type {}; +++ +++ template +++ struct is_range_impl()))>> : std::true_type {}; +++ } // namespace Detail +++ +++ template +++ struct is_range : Detail::is_range_impl {}; +++ +++#if defined(_MANAGED) // Managed types are never ranges +++ template +++ struct is_range { +++ static const bool value = false; +++ }; +++#endif +++ +++ template +++ std::string rangeToString( Range const& range ) { +++ return ::Catch::Detail::rangeToString( begin( range ), end( range ) ); +++ } +++ +++ // Handle vector specially +++ template +++ std::string rangeToString( std::vector const& v ) { +++ ReusableStringStream rss; +++ rss << "{ "; +++ bool first = true; +++ for( bool b : v ) { +++ if( first ) +++ first = false; +++ else +++ rss << ", "; +++ rss << ::Catch::Detail::stringify( b ); +++ } +++ rss << " }"; +++ return rss.str(); +++ } +++ +++ template +++ struct StringMaker::value && !::Catch::Detail::IsStreamInsertable::value>> { +++ static std::string convert( R const& range ) { +++ return rangeToString( range ); +++ } +++ }; +++ +++ template +++ struct StringMaker { +++ static std::string convert(T const(&arr)[SZ]) { +++ return rangeToString(arr); +++ } +++ }; +++ +++ +++} // namespace Catch +++ +++// Separate std::chrono::duration specialization +++#include +++#include +++#include +++ +++ +++namespace Catch { +++ +++template +++struct ratio_string { +++ static std::string symbol() { +++ Catch::ReusableStringStream rss; +++ rss << '[' << Ratio::num << '/' +++ << Ratio::den << ']'; +++ return rss.str(); +++ } +++}; +++ +++template <> +++struct ratio_string { +++ static char symbol() { return 'a'; } +++}; +++template <> +++struct ratio_string { +++ static char symbol() { return 'f'; } +++}; +++template <> +++struct ratio_string { +++ static char symbol() { return 'p'; } +++}; +++template <> +++struct ratio_string { +++ static char symbol() { return 'n'; } +++}; +++template <> +++struct ratio_string { +++ static char symbol() { return 'u'; } +++}; +++template <> +++struct ratio_string { +++ static char symbol() { return 'm'; } +++}; +++ +++ //////////// +++ // std::chrono::duration specializations +++ template +++ struct StringMaker> { +++ static std::string convert(std::chrono::duration const& duration) { +++ ReusableStringStream rss; +++ rss << duration.count() << ' ' << ratio_string::symbol() << 's'; +++ return rss.str(); +++ } +++ }; +++ template +++ struct StringMaker>> { +++ static std::string convert(std::chrono::duration> const& duration) { +++ ReusableStringStream rss; +++ rss << duration.count() << " s"; +++ return rss.str(); +++ } +++ }; +++ template +++ struct StringMaker>> { +++ static std::string convert(std::chrono::duration> const& duration) { +++ ReusableStringStream rss; +++ rss << duration.count() << " m"; +++ return rss.str(); +++ } +++ }; +++ template +++ struct StringMaker>> { +++ static std::string convert(std::chrono::duration> const& duration) { +++ ReusableStringStream rss; +++ rss << duration.count() << " h"; +++ return rss.str(); +++ } +++ }; +++ +++ //////////// +++ // std::chrono::time_point specialization +++ // Generic time_point cannot be specialized, only std::chrono::time_point +++ template +++ struct StringMaker> { +++ static std::string convert(std::chrono::time_point const& time_point) { +++ return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch"; +++ } +++ }; +++ // std::chrono::time_point specialization +++ template +++ struct StringMaker> { +++ static std::string convert(std::chrono::time_point const& time_point) { +++ auto converted = std::chrono::system_clock::to_time_t(time_point); +++ +++#ifdef _MSC_VER +++ std::tm timeInfo = {}; +++ gmtime_s(&timeInfo, &converted); +++#else +++ std::tm* timeInfo = std::gmtime(&converted); +++#endif +++ +++ auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); +++ char timeStamp[timeStampSize]; +++ const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; +++ +++#ifdef _MSC_VER +++ std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); +++#else +++ std::strftime(timeStamp, timeStampSize, fmt, timeInfo); +++#endif +++ return std::string(timeStamp, timeStampSize - 1); +++ } +++ }; +++} +++ +++ +++#define INTERNAL_CATCH_REGISTER_ENUM( enumName, ... ) \ +++namespace Catch { \ +++ template<> struct StringMaker { \ +++ static std::string convert( enumName value ) { \ +++ static const auto& enumInfo = ::Catch::getMutableRegistryHub().getMutableEnumValuesRegistry().registerEnum( #enumName, #__VA_ARGS__, { __VA_ARGS__ } ); \ +++ return static_cast(enumInfo.lookup( static_cast( value ) )); \ +++ } \ +++ }; \ +++} +++ +++#define CATCH_REGISTER_ENUM( enumName, ... ) INTERNAL_CATCH_REGISTER_ENUM( enumName, __VA_ARGS__ ) +++ +++#ifdef _MSC_VER +++#pragma warning(pop) +++#endif +++ +++#endif // CATCH_TOSTRING_HPP_INCLUDED +++ +++#include +++ +++namespace Catch { +++ +++ class Approx { +++ private: +++ bool equalityComparisonImpl(double other) const; +++ // Sets and validates the new margin (margin >= 0) +++ void setMargin(double margin); +++ // Sets and validates the new epsilon (0 < epsilon < 1) +++ void setEpsilon(double epsilon); +++ +++ public: +++ explicit Approx ( double value ); +++ +++ static Approx custom(); +++ +++ Approx operator-() const; +++ +++ template ::value>> +++ Approx operator()( T const& value ) const { +++ Approx approx( static_cast(value) ); +++ approx.m_epsilon = m_epsilon; +++ approx.m_margin = m_margin; +++ approx.m_scale = m_scale; +++ return approx; +++ } +++ +++ template ::value>> +++ explicit Approx( T const& value ): Approx(static_cast(value)) +++ {} +++ +++ +++ template ::value>> +++ friend bool operator == ( const T& lhs, Approx const& rhs ) { +++ auto lhs_v = static_cast(lhs); +++ return rhs.equalityComparisonImpl(lhs_v); +++ } +++ +++ template ::value>> +++ friend bool operator == ( Approx const& lhs, const T& rhs ) { +++ return operator==( rhs, lhs ); +++ } +++ +++ template ::value>> +++ friend bool operator != ( T const& lhs, Approx const& rhs ) { +++ return !operator==( lhs, rhs ); +++ } +++ +++ template ::value>> +++ friend bool operator != ( Approx const& lhs, T const& rhs ) { +++ return !operator==( rhs, lhs ); +++ } +++ +++ template ::value>> +++ friend bool operator <= ( T const& lhs, Approx const& rhs ) { +++ return static_cast(lhs) < rhs.m_value || lhs == rhs; +++ } +++ +++ template ::value>> +++ friend bool operator <= ( Approx const& lhs, T const& rhs ) { +++ return lhs.m_value < static_cast(rhs) || lhs == rhs; +++ } +++ +++ template ::value>> +++ friend bool operator >= ( T const& lhs, Approx const& rhs ) { +++ return static_cast(lhs) > rhs.m_value || lhs == rhs; +++ } +++ +++ template ::value>> +++ friend bool operator >= ( Approx const& lhs, T const& rhs ) { +++ return lhs.m_value > static_cast(rhs) || lhs == rhs; +++ } +++ +++ template ::value>> +++ Approx& epsilon( T const& newEpsilon ) { +++ const auto epsilonAsDouble = static_cast(newEpsilon); +++ setEpsilon(epsilonAsDouble); +++ return *this; +++ } +++ +++ template ::value>> +++ Approx& margin( T const& newMargin ) { +++ const auto marginAsDouble = static_cast(newMargin); +++ setMargin(marginAsDouble); +++ return *this; +++ } +++ +++ template ::value>> +++ Approx& scale( T const& newScale ) { +++ m_scale = static_cast(newScale); +++ return *this; +++ } +++ +++ std::string toString() const; +++ +++ private: +++ double m_epsilon; +++ double m_margin; +++ double m_scale; +++ double m_value; +++ }; +++ +++namespace literals { +++ Approx operator ""_a(long double val); +++ Approx operator ""_a(unsigned long long val); +++} // end namespace literals +++ +++template<> +++struct StringMaker { +++ static std::string convert(Catch::Approx const& value); +++}; +++ +++} // end namespace Catch +++ +++#endif // CATCH_APPROX_HPP_INCLUDED +++ +++ +++#ifndef CATCH_ASSERTION_INFO_HPP_INCLUDED +++#define CATCH_ASSERTION_INFO_HPP_INCLUDED +++ +++ +++ +++#ifndef CATCH_SOURCE_LINE_INFO_HPP_INCLUDED +++#define CATCH_SOURCE_LINE_INFO_HPP_INCLUDED +++ +++#include +++#include +++ +++namespace Catch { +++ +++ struct SourceLineInfo { +++ +++ SourceLineInfo() = delete; +++ constexpr SourceLineInfo( char const* _file, std::size_t _line ) noexcept: +++ file( _file ), +++ line( _line ) +++ {} +++ +++ bool operator == ( SourceLineInfo const& other ) const noexcept; +++ bool operator < ( SourceLineInfo const& other ) const noexcept; +++ +++ char const* file; +++ std::size_t line; +++ +++ friend std::ostream& operator << (std::ostream& os, SourceLineInfo const& info); +++ }; +++} +++ +++#define CATCH_INTERNAL_LINEINFO \ +++ ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) +++ +++#endif // CATCH_SOURCE_LINE_INFO_HPP_INCLUDED +++ +++namespace Catch { +++ +++ struct AssertionInfo { +++ // AssertionInfo() = delete; +++ +++ StringRef macroName; +++ SourceLineInfo lineInfo; +++ StringRef capturedExpression; +++ ResultDisposition::Flags resultDisposition; +++ }; +++ +++} // end namespace Catch +++ +++#endif // CATCH_ASSERTION_INFO_HPP_INCLUDED +++ +++ +++#ifndef CATCH_ASSERTION_RESULT_HPP_INCLUDED +++#define CATCH_ASSERTION_RESULT_HPP_INCLUDED +++ +++ +++ +++#ifndef CATCH_LAZY_EXPR_HPP_INCLUDED +++#define CATCH_LAZY_EXPR_HPP_INCLUDED +++ +++#include +++ +++namespace Catch { +++ +++ class ITransientExpression; +++ +++ class LazyExpression { +++ friend class AssertionHandler; +++ friend struct AssertionStats; +++ friend class RunContext; +++ +++ ITransientExpression const* m_transientExpression = nullptr; +++ bool m_isNegated; +++ public: +++ LazyExpression( bool isNegated ): +++ m_isNegated(isNegated) +++ {} +++ LazyExpression(LazyExpression const& other) = default; +++ LazyExpression& operator = ( LazyExpression const& ) = delete; +++ +++ explicit operator bool() const { +++ return m_transientExpression != nullptr; +++ } +++ +++ friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&; +++ }; +++ +++} // namespace Catch +++ +++#endif // CATCH_LAZY_EXPR_HPP_INCLUDED +++ +++#include +++ +++namespace Catch { +++ +++ struct AssertionResultData +++ { +++ AssertionResultData() = delete; +++ +++ AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression ); +++ +++ std::string message; +++ mutable std::string reconstructedExpression; +++ LazyExpression lazyExpression; +++ ResultWas::OfType resultType; +++ +++ std::string reconstructExpression() const; +++ }; +++ +++ class AssertionResult { +++ public: +++ AssertionResult() = delete; +++ AssertionResult( AssertionInfo const& info, AssertionResultData&& data ); +++ +++ bool isOk() const; +++ bool succeeded() const; +++ ResultWas::OfType getResultType() const; +++ bool hasExpression() const; +++ bool hasMessage() const; +++ std::string getExpression() const; +++ std::string getExpressionInMacro() const; +++ bool hasExpandedExpression() const; +++ std::string getExpandedExpression() const; +++ StringRef getMessage() const; +++ SourceLineInfo getSourceInfo() const; +++ StringRef getTestMacroName() const; +++ +++ //protected: +++ AssertionInfo m_info; +++ AssertionResultData m_resultData; +++ }; +++ +++} // end namespace Catch +++ +++#endif // CATCH_ASSERTION_RESULT_HPP_INCLUDED +++ +++ +++#ifndef CATCH_CONFIG_HPP_INCLUDED +++#define CATCH_CONFIG_HPP_INCLUDED +++ +++ +++ +++#ifndef CATCH_TEST_SPEC_HPP_INCLUDED +++#define CATCH_TEST_SPEC_HPP_INCLUDED +++ +++#ifdef __clang__ +++#pragma clang diagnostic push +++#pragma clang diagnostic ignored "-Wpadded" +++#endif +++ +++ +++ +++#ifndef CATCH_WILDCARD_PATTERN_HPP_INCLUDED +++#define CATCH_WILDCARD_PATTERN_HPP_INCLUDED +++ +++ +++ +++#ifndef CATCH_CASE_SENSITIVE_HPP_INCLUDED +++#define CATCH_CASE_SENSITIVE_HPP_INCLUDED +++ +++namespace Catch { +++ +++ enum class CaseSensitive { Yes, No }; +++ +++} // namespace Catch +++ +++#endif // CATCH_CASE_SENSITIVE_HPP_INCLUDED +++ +++#include +++ +++namespace Catch +++{ +++ class WildcardPattern { +++ enum WildcardPosition { +++ NoWildcard = 0, +++ WildcardAtStart = 1, +++ WildcardAtEnd = 2, +++ WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd +++ }; +++ +++ public: +++ +++ WildcardPattern( std::string const& pattern, CaseSensitive caseSensitivity ); +++ bool matches( std::string const& str ) const; +++ +++ private: +++ std::string normaliseString( std::string const& str ) const; +++ CaseSensitive m_caseSensitivity; +++ WildcardPosition m_wildcard = NoWildcard; +++ std::string m_pattern; +++ }; +++} +++ +++#endif // CATCH_WILDCARD_PATTERN_HPP_INCLUDED +++ +++#include +++#include +++#include +++ +++namespace Catch { +++ +++ class IConfig; +++ struct TestCaseInfo; +++ class TestCaseHandle; +++ +++ class TestSpec { +++ +++ class Pattern { +++ public: +++ explicit Pattern( std::string const& name ); +++ virtual ~Pattern(); +++ virtual bool matches( TestCaseInfo const& testCase ) const = 0; +++ std::string const& name() const; +++ private: +++ virtual void serializeTo( std::ostream& out ) const = 0; +++ // Writes string that would be reparsed into the pattern +++ friend std::ostream& operator<<(std::ostream& out, +++ Pattern const& pattern) { +++ pattern.serializeTo( out ); +++ return out; +++ } +++ +++ std::string const m_name; +++ }; +++ +++ class NamePattern : public Pattern { +++ public: +++ explicit NamePattern( std::string const& name, std::string const& filterString ); +++ bool matches( TestCaseInfo const& testCase ) const override; +++ private: +++ void serializeTo( std::ostream& out ) const override; +++ +++ WildcardPattern m_wildcardPattern; +++ }; +++ +++ class TagPattern : public Pattern { +++ public: +++ explicit TagPattern( std::string const& tag, std::string const& filterString ); +++ bool matches( TestCaseInfo const& testCase ) const override; +++ private: +++ void serializeTo( std::ostream& out ) const override; +++ +++ std::string m_tag; +++ }; +++ +++ struct Filter { +++ std::vector> m_required; +++ std::vector> m_forbidden; +++ +++ //! Serializes this filter into a string that would be parsed into +++ //! an equivalent filter +++ void serializeTo( std::ostream& out ) const; +++ friend std::ostream& operator<<(std::ostream& out, Filter const& f) { +++ f.serializeTo( out ); +++ return out; +++ } +++ +++ bool matches( TestCaseInfo const& testCase ) const; +++ }; +++ +++ static std::string extractFilterName( Filter const& filter ); +++ +++ public: +++ struct FilterMatch { +++ std::string name; +++ std::vector tests; +++ }; +++ using Matches = std::vector; +++ using vectorStrings = std::vector; +++ +++ bool hasFilters() const; +++ bool matches( TestCaseInfo const& testCase ) const; +++ Matches matchesByFilter( std::vector const& testCases, IConfig const& config ) const; +++ const vectorStrings & getInvalidSpecs() const; +++ +++ private: +++ std::vector m_filters; +++ std::vector m_invalidSpecs; +++ +++ friend class TestSpecParser; +++ //! Serializes this test spec into a string that would be parsed into +++ //! equivalent test spec +++ void serializeTo( std::ostream& out ) const; +++ friend std::ostream& operator<<(std::ostream& out, +++ TestSpec const& spec) { +++ spec.serializeTo( out ); +++ return out; +++ } +++ }; +++} +++ +++#ifdef __clang__ +++#pragma clang diagnostic pop +++#endif +++ +++#endif // CATCH_TEST_SPEC_HPP_INCLUDED +++ +++ +++#ifndef CATCH_OPTIONAL_HPP_INCLUDED +++#define CATCH_OPTIONAL_HPP_INCLUDED +++ +++ +++#include +++ +++namespace Catch { +++ +++ // An optional type +++ template +++ class Optional { +++ public: +++ Optional(): nullableValue( nullptr ) {} +++ ~Optional() { reset(); } +++ +++ Optional( T const& _value ): +++ nullableValue( new ( storage ) T( _value ) ) {} +++ Optional( T&& _value ): +++ nullableValue( new ( storage ) T( CATCH_MOVE( _value ) ) ) {} +++ +++ Optional& operator=( T const& _value ) { +++ reset(); +++ nullableValue = new ( storage ) T( _value ); +++ return *this; +++ } +++ Optional& operator=( T&& _value ) { +++ reset(); +++ nullableValue = new ( storage ) T( CATCH_MOVE( _value ) ); +++ return *this; +++ } +++ +++ Optional( Optional const& _other ): +++ nullableValue( _other ? new ( storage ) T( *_other ) : nullptr ) {} +++ Optional( Optional&& _other ): +++ nullableValue( _other ? new ( storage ) T( CATCH_MOVE( *_other ) ) +++ : nullptr ) {} +++ +++ Optional& operator=( Optional const& _other ) { +++ if ( &_other != this ) { +++ reset(); +++ if ( _other ) { nullableValue = new ( storage ) T( *_other ); } +++ } +++ return *this; +++ } +++ Optional& operator=( Optional&& _other ) { +++ if ( &_other != this ) { +++ reset(); +++ if ( _other ) { +++ nullableValue = new ( storage ) T( CATCH_MOVE( *_other ) ); +++ } +++ } +++ return *this; +++ } +++ +++ void reset() { +++ if ( nullableValue ) { nullableValue->~T(); } +++ nullableValue = nullptr; +++ } +++ +++ T& operator*() { +++ assert(nullableValue); +++ return *nullableValue; +++ } +++ T const& operator*() const { +++ assert(nullableValue); +++ return *nullableValue; +++ } +++ T* operator->() { +++ assert(nullableValue); +++ return nullableValue; +++ } +++ const T* operator->() const { +++ assert(nullableValue); +++ return nullableValue; +++ } +++ +++ T valueOr( T const& defaultValue ) const { +++ return nullableValue ? *nullableValue : defaultValue; +++ } +++ +++ bool some() const { return nullableValue != nullptr; } +++ bool none() const { return nullableValue == nullptr; } +++ +++ bool operator !() const { return nullableValue == nullptr; } +++ explicit operator bool() const { +++ return some(); +++ } +++ +++ friend bool operator==(Optional const& a, Optional const& b) { +++ if (a.none() && b.none()) { +++ return true; +++ } else if (a.some() && b.some()) { +++ return *a == *b; +++ } else { +++ return false; +++ } +++ } +++ friend bool operator!=(Optional const& a, Optional const& b) { +++ return !( a == b ); +++ } +++ +++ private: +++ T* nullableValue; +++ alignas(alignof(T)) char storage[sizeof(T)]; +++ }; +++ +++} // end namespace Catch +++ +++#endif // CATCH_OPTIONAL_HPP_INCLUDED +++ +++ +++#ifndef CATCH_RANDOM_SEED_GENERATION_HPP_INCLUDED +++#define CATCH_RANDOM_SEED_GENERATION_HPP_INCLUDED +++ +++#include +++ +++namespace Catch { +++ +++ enum class GenerateFrom { +++ Time, +++ RandomDevice, +++ //! Currently equivalent to RandomDevice, but can change at any point +++ Default +++ }; +++ +++ std::uint32_t generateRandomSeed(GenerateFrom from); +++ +++} // end namespace Catch +++ +++#endif // CATCH_RANDOM_SEED_GENERATION_HPP_INCLUDED +++ +++ +++#ifndef CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED +++#define CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED +++ +++ +++#include +++#include +++#include +++ +++namespace Catch { +++ +++ enum class ColourMode : std::uint8_t; +++ +++ namespace Detail { +++ //! Splits the reporter spec into reporter name and kv-pair options +++ std::vector splitReporterSpec( StringRef reporterSpec ); +++ +++ Optional stringToColourMode( StringRef colourMode ); +++ } +++ +++ /** +++ * Structured reporter spec that a reporter can be created from +++ * +++ * Parsing has been validated, but semantics have not. This means e.g. +++ * that the colour mode is known to Catch2, but it might not be +++ * compiled into the binary, and the output filename might not be +++ * openable. +++ */ +++ class ReporterSpec { +++ std::string m_name; +++ Optional m_outputFileName; +++ Optional m_colourMode; +++ std::map m_customOptions; +++ +++ friend bool operator==( ReporterSpec const& lhs, +++ ReporterSpec const& rhs ); +++ friend bool operator!=( ReporterSpec const& lhs, +++ ReporterSpec const& rhs ) { +++ return !( lhs == rhs ); +++ } +++ +++ public: +++ ReporterSpec( +++ std::string name, +++ Optional outputFileName, +++ Optional colourMode, +++ std::map customOptions ); +++ +++ std::string const& name() const { return m_name; } +++ +++ Optional const& outputFile() const { +++ return m_outputFileName; +++ } +++ +++ Optional const& colourMode() const { return m_colourMode; } +++ +++ std::map const& customOptions() const { +++ return m_customOptions; +++ } +++ }; +++ +++ /** +++ * Parses provided reporter spec string into +++ * +++ * Returns empty optional on errors, e.g. +++ * * field that is not first and not a key+value pair +++ * * duplicated keys in kv pair +++ * * unknown catch reporter option +++ * * empty key/value in an custom kv pair +++ * * ... +++ */ +++ Optional parseReporterSpec( StringRef reporterSpec ); +++ +++} +++ +++#endif // CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED +++ +++#include +++#include +++#include +++#include +++ +++namespace Catch { +++ +++ class IStream; +++ +++ /** +++ * `ReporterSpec` but with the defaults filled in. +++ * +++ * Like `ReporterSpec`, the semantics are unchecked. +++ */ +++ struct ProcessedReporterSpec { +++ std::string name; +++ std::string outputFilename; +++ ColourMode colourMode; +++ std::map customOptions; +++ friend bool operator==( ProcessedReporterSpec const& lhs, +++ ProcessedReporterSpec const& rhs ); +++ friend bool operator!=( ProcessedReporterSpec const& lhs, +++ ProcessedReporterSpec const& rhs ) { +++ return !( lhs == rhs ); +++ } +++ }; +++ +++ struct ConfigData { +++ +++ bool listTests = false; +++ bool listTags = false; +++ bool listReporters = false; +++ bool listListeners = false; +++ +++ bool showSuccessfulTests = false; +++ bool shouldDebugBreak = false; +++ bool noThrow = false; +++ bool showHelp = false; +++ bool showInvisibles = false; +++ bool filenamesAsTags = false; +++ bool libIdentify = false; +++ bool allowZeroTests = false; +++ +++ int abortAfter = -1; +++ uint32_t rngSeed = generateRandomSeed(GenerateFrom::Default); +++ +++ unsigned int shardCount = 1; +++ unsigned int shardIndex = 0; +++ +++ bool skipBenchmarks = false; +++ bool benchmarkNoAnalysis = false; +++ unsigned int benchmarkSamples = 100; +++ double benchmarkConfidenceInterval = 0.95; +++ unsigned int benchmarkResamples = 100'000; +++ std::chrono::milliseconds::rep benchmarkWarmupTime = 100; +++ +++ Verbosity verbosity = Verbosity::Normal; +++ WarnAbout::What warnings = WarnAbout::Nothing; +++ ShowDurations showDurations = ShowDurations::DefaultForReporter; +++ double minDuration = -1; +++ TestRunOrder runOrder = TestRunOrder::Declared; +++ ColourMode defaultColourMode = ColourMode::PlatformDefault; +++ WaitForKeypress::When waitForKeypress = WaitForKeypress::Never; +++ +++ std::string defaultOutputFilename; +++ std::string name; +++ std::string processName; +++ std::vector reporterSpecifications; +++ +++ std::vector testsOrTags; +++ std::vector sectionsToRun; +++ }; +++ +++ +++ class Config : public IConfig { +++ public: +++ +++ Config() = default; +++ Config( ConfigData const& data ); +++ ~Config() override; // = default in the cpp file +++ +++ bool listTests() const; +++ bool listTags() const; +++ bool listReporters() const; +++ bool listListeners() const; +++ +++ std::vector const& getReporterSpecs() const; +++ std::vector const& +++ getProcessedReporterSpecs() const; +++ +++ std::vector const& getTestsOrTags() const override; +++ std::vector const& getSectionsToRun() const override; +++ +++ TestSpec const& testSpec() const override; +++ bool hasTestFilters() const override; +++ +++ bool showHelp() const; +++ +++ // IConfig interface +++ bool allowThrows() const override; +++ StringRef name() const override; +++ bool includeSuccessfulResults() const override; +++ bool warnAboutMissingAssertions() const override; +++ bool warnAboutUnmatchedTestSpecs() const override; +++ bool zeroTestsCountAsSuccess() const override; +++ ShowDurations showDurations() const override; +++ double minDuration() const override; +++ TestRunOrder runOrder() const override; +++ uint32_t rngSeed() const override; +++ unsigned int shardCount() const override; +++ unsigned int shardIndex() const override; +++ ColourMode defaultColourMode() const override; +++ bool shouldDebugBreak() const override; +++ int abortAfter() const override; +++ bool showInvisibles() const override; +++ Verbosity verbosity() const override; +++ bool skipBenchmarks() const override; +++ bool benchmarkNoAnalysis() const override; +++ unsigned int benchmarkSamples() const override; +++ double benchmarkConfidenceInterval() const override; +++ unsigned int benchmarkResamples() const override; +++ std::chrono::milliseconds benchmarkWarmupTime() const override; +++ +++ private: +++ // Reads Bazel env vars and applies them to the config +++ void readBazelEnvVars(); +++ +++ ConfigData m_data; +++ std::vector m_processedReporterSpecs; +++ TestSpec m_testSpec; +++ bool m_hasTestFilters = false; +++ }; +++} // end namespace Catch +++ +++#endif // CATCH_CONFIG_HPP_INCLUDED +++ +++ +++#ifndef CATCH_GET_RANDOM_SEED_HPP_INCLUDED +++#define CATCH_GET_RANDOM_SEED_HPP_INCLUDED +++ +++#include +++ +++namespace Catch { +++ //! Returns Catch2's current RNG seed. +++ std::uint32_t getSeed(); +++} +++ +++#endif // CATCH_GET_RANDOM_SEED_HPP_INCLUDED +++ +++ +++#ifndef CATCH_MESSAGE_HPP_INCLUDED +++#define CATCH_MESSAGE_HPP_INCLUDED +++ +++ +++ +++ +++/** \file +++ * Wrapper for the CATCH_CONFIG_PREFIX_MESSAGES configuration option +++ * +++ * CATCH_CONFIG_PREFIX_ALL can be used to avoid clashes with other macros +++ * by prepending CATCH_. This may not be desirable if the only clashes are with +++ * logger macros such as INFO and WARN. In this cases +++ * CATCH_CONFIG_PREFIX_MESSAGES can be used to only prefix a small subset +++ * of relevant macros. +++ * +++ */ +++ +++#ifndef CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED +++#define CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED +++ +++ +++#if defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_PREFIX_MESSAGES) +++ #define CATCH_CONFIG_PREFIX_MESSAGES +++#endif +++ +++#endif // CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED +++ +++ +++#ifndef CATCH_STREAM_END_STOP_HPP_INCLUDED +++#define CATCH_STREAM_END_STOP_HPP_INCLUDED +++ +++ +++namespace Catch { +++ +++ // Use this in variadic streaming macros to allow +++ // << +StreamEndStop +++ // as well as +++ // << stuff +StreamEndStop +++ struct StreamEndStop { +++ constexpr StringRef operator+() const { return StringRef(); } +++ +++ template +++ constexpr friend T const& operator+( T const& value, StreamEndStop ) { +++ return value; +++ } +++ }; +++ +++} // namespace Catch +++ +++#endif // CATCH_STREAM_END_STOP_HPP_INCLUDED +++ +++ +++#ifndef CATCH_MESSAGE_INFO_HPP_INCLUDED +++#define CATCH_MESSAGE_INFO_HPP_INCLUDED +++ +++ +++#include +++ +++namespace Catch { +++ +++ struct MessageInfo { +++ MessageInfo( StringRef _macroName, +++ SourceLineInfo const& _lineInfo, +++ ResultWas::OfType _type ); +++ +++ StringRef macroName; +++ std::string message; +++ SourceLineInfo lineInfo; +++ ResultWas::OfType type; +++ unsigned int sequence; +++ +++ bool operator == (MessageInfo const& other) const { +++ return sequence == other.sequence; +++ } +++ bool operator < (MessageInfo const& other) const { +++ return sequence < other.sequence; +++ } +++ private: +++ static unsigned int globalCount; +++ }; +++ +++} // end namespace Catch +++ +++#endif // CATCH_MESSAGE_INFO_HPP_INCLUDED +++ +++#include +++#include +++ +++namespace Catch { +++ +++ struct SourceLineInfo; +++ class IResultCapture; +++ +++ struct MessageStream { +++ +++ template +++ MessageStream& operator << ( T const& value ) { +++ m_stream << value; +++ return *this; +++ } +++ +++ ReusableStringStream m_stream; +++ }; +++ +++ struct MessageBuilder : MessageStream { +++ MessageBuilder( StringRef macroName, +++ SourceLineInfo const& lineInfo, +++ ResultWas::OfType type ): +++ m_info(macroName, lineInfo, type) {} +++ +++ template +++ MessageBuilder&& operator << ( T const& value ) && { +++ m_stream << value; +++ return CATCH_MOVE(*this); +++ } +++ +++ MessageInfo m_info; +++ }; +++ +++ class ScopedMessage { +++ public: +++ explicit ScopedMessage( MessageBuilder&& builder ); +++ ScopedMessage( ScopedMessage& duplicate ) = delete; +++ ScopedMessage( ScopedMessage&& old ) noexcept; +++ ~ScopedMessage(); +++ +++ MessageInfo m_info; +++ bool m_moved = false; +++ }; +++ +++ class Capturer { +++ std::vector m_messages; +++ IResultCapture& m_resultCapture; +++ size_t m_captured = 0; +++ public: +++ Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ); +++ +++ Capturer(Capturer const&) = delete; +++ Capturer& operator=(Capturer const&) = delete; +++ +++ ~Capturer(); +++ +++ void captureValue( size_t index, std::string const& value ); +++ +++ template +++ void captureValues( size_t index, T const& value ) { +++ captureValue( index, Catch::Detail::stringify( value ) ); +++ } +++ +++ template +++ void captureValues( size_t index, T const& value, Ts const&... values ) { +++ captureValue( index, Catch::Detail::stringify(value) ); +++ captureValues( index+1, values... ); +++ } +++ }; +++ +++} // end namespace Catch +++ +++/////////////////////////////////////////////////////////////////////////////// +++#define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \ +++ do { \ +++ Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::StringRef(), resultDisposition ); \ +++ catchAssertionHandler.handleMessage( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \ +++ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ +++ } while( false ) +++ +++/////////////////////////////////////////////////////////////////////////////// +++#define INTERNAL_CATCH_CAPTURE( varName, macroName, ... ) \ +++ Catch::Capturer varName( macroName##_catch_sr, \ +++ CATCH_INTERNAL_LINEINFO, \ +++ Catch::ResultWas::Info, \ +++ #__VA_ARGS__##_catch_sr ); \ +++ varName.captureValues( 0, __VA_ARGS__ ) +++ +++/////////////////////////////////////////////////////////////////////////////// +++#define INTERNAL_CATCH_INFO( macroName, log ) \ +++ const Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage )( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ) +++ +++/////////////////////////////////////////////////////////////////////////////// +++#define INTERNAL_CATCH_UNSCOPED_INFO( macroName, log ) \ +++ Catch::getResultCapture().emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ) +++ +++ +++#if defined(CATCH_CONFIG_PREFIX_MESSAGES) && !defined(CATCH_CONFIG_DISABLE) +++ +++ #define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) +++ #define CATCH_UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "CATCH_UNSCOPED_INFO", msg ) +++ #define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) +++ #define CATCH_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE", __VA_ARGS__ ) +++ +++#elif defined(CATCH_CONFIG_PREFIX_MESSAGES) && defined(CATCH_CONFIG_DISABLE) +++ +++ #define CATCH_INFO( msg ) (void)(0) +++ #define CATCH_UNSCOPED_INFO( msg ) (void)(0) +++ #define CATCH_WARN( msg ) (void)(0) +++ #define CATCH_CAPTURE( ... ) (void)(0) +++ +++#elif !defined(CATCH_CONFIG_PREFIX_MESSAGES) && !defined(CATCH_CONFIG_DISABLE) +++ +++ #define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) +++ #define UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "UNSCOPED_INFO", msg ) +++ #define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) +++ #define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE", __VA_ARGS__ ) +++ +++#elif !defined(CATCH_CONFIG_PREFIX_MESSAGES) && defined(CATCH_CONFIG_DISABLE) +++ +++ #define INFO( msg ) (void)(0) +++ #define UNSCOPED_INFO( msg ) (void)(0) +++ #define WARN( msg ) (void)(0) +++ #define CAPTURE( ... ) (void)(0) +++ +++#endif // end of user facing macro declarations +++ +++ +++ +++ +++#endif // CATCH_MESSAGE_HPP_INCLUDED +++ +++ +++#ifndef CATCH_SECTION_INFO_HPP_INCLUDED +++#define CATCH_SECTION_INFO_HPP_INCLUDED +++ +++ +++ +++#ifndef CATCH_TOTALS_HPP_INCLUDED +++#define CATCH_TOTALS_HPP_INCLUDED +++ +++#include +++ +++namespace Catch { +++ +++ struct Counts { +++ Counts operator - ( Counts const& other ) const; +++ Counts& operator += ( Counts const& other ); +++ +++ std::uint64_t total() const; +++ bool allPassed() const; +++ bool allOk() const; +++ +++ std::uint64_t passed = 0; +++ std::uint64_t failed = 0; +++ std::uint64_t failedButOk = 0; +++ std::uint64_t skipped = 0; +++ }; +++ +++ struct Totals { +++ +++ Totals operator - ( Totals const& other ) const; +++ Totals& operator += ( Totals const& other ); +++ +++ Totals delta( Totals const& prevTotals ) const; +++ +++ Counts assertions; +++ Counts testCases; +++ }; +++} +++ +++#endif // CATCH_TOTALS_HPP_INCLUDED +++ +++#include +++ +++namespace Catch { +++ +++ struct SectionInfo { +++ // The last argument is ignored, so that people can write +++ // SECTION("ShortName", "Proper description that is long") and +++ // still use the `-c` flag comfortably. +++ SectionInfo( SourceLineInfo const& _lineInfo, std::string _name, +++ const char* const = nullptr ): +++ name(CATCH_MOVE(_name)), +++ lineInfo(_lineInfo) +++ {} +++ +++ std::string name; +++ SourceLineInfo lineInfo; +++ }; +++ +++ struct SectionEndInfo { +++ SectionInfo sectionInfo; +++ Counts prevAssertions; +++ double durationInSeconds; +++ }; +++ +++} // end namespace Catch +++ +++#endif // CATCH_SECTION_INFO_HPP_INCLUDED +++ +++ +++#ifndef CATCH_SESSION_HPP_INCLUDED +++#define CATCH_SESSION_HPP_INCLUDED +++ +++ +++ +++#ifndef CATCH_COMMANDLINE_HPP_INCLUDED +++#define CATCH_COMMANDLINE_HPP_INCLUDED +++ +++ +++ +++#ifndef CATCH_CLARA_HPP_INCLUDED +++#define CATCH_CLARA_HPP_INCLUDED +++ +++#if defined( __clang__ ) +++# pragma clang diagnostic push +++# pragma clang diagnostic ignored "-Wweak-vtables" +++# pragma clang diagnostic ignored "-Wshadow" +++# pragma clang diagnostic ignored "-Wdeprecated" +++#endif +++ +++#if defined( __GNUC__ ) +++# pragma GCC diagnostic push +++# pragma GCC diagnostic ignored "-Wsign-conversion" +++#endif +++ +++#ifndef CLARA_CONFIG_OPTIONAL_TYPE +++# ifdef __has_include +++# if __has_include( ) && __cplusplus >= 201703L +++# include +++# define CLARA_CONFIG_OPTIONAL_TYPE std::optional +++# endif +++# endif +++#endif +++ +++ +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++ +++namespace Catch { +++ namespace Clara { +++ +++ class Args; +++ class Parser; +++ +++ // enum of result types from a parse +++ enum class ParseResultType { +++ Matched, +++ NoMatch, +++ ShortCircuitAll, +++ ShortCircuitSame +++ }; +++ +++ struct accept_many_t {}; +++ constexpr accept_many_t accept_many {}; +++ +++ namespace Detail { +++ struct fake_arg { +++ template +++ operator T(); +++ }; +++ +++ template +++ struct is_unary_function : std::false_type {}; +++ +++ template +++ struct is_unary_function< +++ F, +++ Catch::Detail::void_t()( fake_arg() ) ) +++ > +++ > : std::true_type {}; +++ +++ // Traits for extracting arg and return type of lambdas (for single +++ // argument lambdas) +++ template +++ struct UnaryLambdaTraits +++ : UnaryLambdaTraits {}; +++ +++ template +++ struct UnaryLambdaTraits { +++ static const bool isValid = false; +++ }; +++ +++ template +++ struct UnaryLambdaTraits { +++ static const bool isValid = true; +++ using ArgType = std::remove_const_t>; +++ using ReturnType = ReturnT; +++ }; +++ +++ class TokenStream; +++ +++ // Wraps a token coming from a token stream. These may not directly +++ // correspond to strings as a single string may encode an option + +++ // its argument if the : or = form is used +++ enum class TokenType { Option, Argument }; +++ struct Token { +++ TokenType type; +++ StringRef token; +++ }; +++ +++ // Abstracts iterators into args as a stream of tokens, with option +++ // arguments uniformly handled +++ class TokenStream { +++ using Iterator = std::vector::const_iterator; +++ Iterator it; +++ Iterator itEnd; +++ std::vector m_tokenBuffer; +++ void loadBuffer(); +++ +++ public: +++ explicit TokenStream( Args const& args ); +++ TokenStream( Iterator it, Iterator itEnd ); +++ +++ explicit operator bool() const { +++ return !m_tokenBuffer.empty() || it != itEnd; +++ } +++ +++ size_t count() const { +++ return m_tokenBuffer.size() + ( itEnd - it ); +++ } +++ +++ Token operator*() const { +++ assert( !m_tokenBuffer.empty() ); +++ return m_tokenBuffer.front(); +++ } +++ +++ Token const* operator->() const { +++ assert( !m_tokenBuffer.empty() ); +++ return &m_tokenBuffer.front(); +++ } +++ +++ TokenStream& operator++(); +++ }; +++ +++ //! Denotes type of a parsing result +++ enum class ResultType { +++ Ok, ///< No errors +++ LogicError, ///< Error in user-specified arguments for +++ ///< construction +++ RuntimeError ///< Error in parsing inputs +++ }; +++ +++ class ResultBase { +++ protected: +++ ResultBase( ResultType type ): m_type( type ) {} +++ virtual ~ResultBase(); // = default; +++ +++ +++ ResultBase(ResultBase const&) = default; +++ ResultBase& operator=(ResultBase const&) = default; +++ ResultBase(ResultBase&&) = default; +++ ResultBase& operator=(ResultBase&&) = default; +++ +++ virtual void enforceOk() const = 0; +++ +++ ResultType m_type; +++ }; +++ +++ template +++ class ResultValueBase : public ResultBase { +++ public: +++ T const& value() const& { +++ enforceOk(); +++ return m_value; +++ } +++ T&& value() && { +++ enforceOk(); +++ return CATCH_MOVE( m_value ); +++ } +++ +++ protected: +++ ResultValueBase( ResultType type ): ResultBase( type ) {} +++ +++ ResultValueBase( ResultValueBase const& other ): +++ ResultBase( other ) { +++ if ( m_type == ResultType::Ok ) +++ new ( &m_value ) T( other.m_value ); +++ } +++ ResultValueBase( ResultValueBase&& other ): +++ ResultBase( other ) { +++ if ( m_type == ResultType::Ok ) +++ new ( &m_value ) T( CATCH_MOVE(other.m_value) ); +++ } +++ +++ +++ ResultValueBase( ResultType, T const& value ): +++ ResultBase( ResultType::Ok ) { +++ new ( &m_value ) T( value ); +++ } +++ ResultValueBase( ResultType, T&& value ): +++ ResultBase( ResultType::Ok ) { +++ new ( &m_value ) T( CATCH_MOVE(value) ); +++ } +++ +++ ResultValueBase& operator=( ResultValueBase const& other ) { +++ if ( m_type == ResultType::Ok ) +++ m_value.~T(); +++ ResultBase::operator=( other ); +++ if ( m_type == ResultType::Ok ) +++ new ( &m_value ) T( other.m_value ); +++ return *this; +++ } +++ ResultValueBase& operator=( ResultValueBase&& other ) { +++ if ( m_type == ResultType::Ok ) m_value.~T(); +++ ResultBase::operator=( other ); +++ if ( m_type == ResultType::Ok ) +++ new ( &m_value ) T( CATCH_MOVE(other.m_value) ); +++ return *this; +++ } +++ +++ +++ ~ResultValueBase() override { +++ if ( m_type == ResultType::Ok ) +++ m_value.~T(); +++ } +++ +++ union { +++ T m_value; +++ }; +++ }; +++ +++ template <> class ResultValueBase : public ResultBase { +++ protected: +++ using ResultBase::ResultBase; +++ }; +++ +++ template +++ class BasicResult : public ResultValueBase { +++ public: +++ template +++ explicit BasicResult( BasicResult const& other ): +++ ResultValueBase( other.type() ), +++ m_errorMessage( other.errorMessage() ) { +++ assert( type() != ResultType::Ok ); +++ } +++ +++ template +++ static auto ok( U&& value ) -> BasicResult { +++ return { ResultType::Ok, CATCH_FORWARD(value) }; +++ } +++ static auto ok() -> BasicResult { return { ResultType::Ok }; } +++ static auto logicError( std::string&& message ) +++ -> BasicResult { +++ return { ResultType::LogicError, CATCH_MOVE(message) }; +++ } +++ static auto runtimeError( std::string&& message ) +++ -> BasicResult { +++ return { ResultType::RuntimeError, CATCH_MOVE(message) }; +++ } +++ +++ explicit operator bool() const { +++ return m_type == ResultType::Ok; +++ } +++ auto type() const -> ResultType { return m_type; } +++ auto errorMessage() const -> std::string const& { +++ return m_errorMessage; +++ } +++ +++ protected: +++ void enforceOk() const override { +++ +++ // Errors shouldn't reach this point, but if they do +++ // the actual error message will be in m_errorMessage +++ assert( m_type != ResultType::LogicError ); +++ assert( m_type != ResultType::RuntimeError ); +++ if ( m_type != ResultType::Ok ) +++ std::abort(); +++ } +++ +++ std::string +++ m_errorMessage; // Only populated if resultType is an error +++ +++ BasicResult( ResultType type, +++ std::string&& message ): +++ ResultValueBase( type ), m_errorMessage( CATCH_MOVE(message) ) { +++ assert( m_type != ResultType::Ok ); +++ } +++ +++ using ResultValueBase::ResultValueBase; +++ using ResultBase::m_type; +++ }; +++ +++ class ParseState { +++ public: +++ ParseState( ParseResultType type, +++ TokenStream remainingTokens ); +++ +++ ParseResultType type() const { return m_type; } +++ TokenStream const& remainingTokens() const& { +++ return m_remainingTokens; +++ } +++ TokenStream&& remainingTokens() && { +++ return CATCH_MOVE( m_remainingTokens ); +++ } +++ +++ private: +++ ParseResultType m_type; +++ TokenStream m_remainingTokens; +++ }; +++ +++ using Result = BasicResult; +++ using ParserResult = BasicResult; +++ using InternalParseResult = BasicResult; +++ +++ struct HelpColumns { +++ std::string left; +++ StringRef descriptions; +++ }; +++ +++ template +++ ParserResult convertInto( std::string const& source, T& target ) { +++ std::stringstream ss( source ); +++ ss >> target; +++ if ( ss.fail() ) { +++ return ParserResult::runtimeError( +++ "Unable to convert '" + source + +++ "' to destination type" ); +++ } else { +++ return ParserResult::ok( ParseResultType::Matched ); +++ } +++ } +++ ParserResult convertInto( std::string const& source, +++ std::string& target ); +++ ParserResult convertInto( std::string const& source, bool& target ); +++ +++#ifdef CLARA_CONFIG_OPTIONAL_TYPE +++ template +++ auto convertInto( std::string const& source, +++ CLARA_CONFIG_OPTIONAL_TYPE& target ) +++ -> ParserResult { +++ T temp; +++ auto result = convertInto( source, temp ); +++ if ( result ) +++ target = CATCH_MOVE( temp ); +++ return result; +++ } +++#endif // CLARA_CONFIG_OPTIONAL_TYPE +++ +++ struct BoundRef : Catch::Detail::NonCopyable { +++ virtual ~BoundRef() = default; +++ virtual bool isContainer() const; +++ virtual bool isFlag() const; +++ }; +++ struct BoundValueRefBase : BoundRef { +++ virtual auto setValue( std::string const& arg ) +++ -> ParserResult = 0; +++ }; +++ struct BoundFlagRefBase : BoundRef { +++ virtual auto setFlag( bool flag ) -> ParserResult = 0; +++ bool isFlag() const override; +++ }; +++ +++ template struct BoundValueRef : BoundValueRefBase { +++ T& m_ref; +++ +++ explicit BoundValueRef( T& ref ): m_ref( ref ) {} +++ +++ ParserResult setValue( std::string const& arg ) override { +++ return convertInto( arg, m_ref ); +++ } +++ }; +++ +++ template +++ struct BoundValueRef> : BoundValueRefBase { +++ std::vector& m_ref; +++ +++ explicit BoundValueRef( std::vector& ref ): m_ref( ref ) {} +++ +++ auto isContainer() const -> bool override { return true; } +++ +++ auto setValue( std::string const& arg ) +++ -> ParserResult override { +++ T temp; +++ auto result = convertInto( arg, temp ); +++ if ( result ) +++ m_ref.push_back( temp ); +++ return result; +++ } +++ }; +++ +++ struct BoundFlagRef : BoundFlagRefBase { +++ bool& m_ref; +++ +++ explicit BoundFlagRef( bool& ref ): m_ref( ref ) {} +++ +++ ParserResult setFlag( bool flag ) override; +++ }; +++ +++ template struct LambdaInvoker { +++ static_assert( +++ std::is_same::value, +++ "Lambda must return void or clara::ParserResult" ); +++ +++ template +++ static auto invoke( L const& lambda, ArgType const& arg ) +++ -> ParserResult { +++ return lambda( arg ); +++ } +++ }; +++ +++ template <> struct LambdaInvoker { +++ template +++ static auto invoke( L const& lambda, ArgType const& arg ) +++ -> ParserResult { +++ lambda( arg ); +++ return ParserResult::ok( ParseResultType::Matched ); +++ } +++ }; +++ +++ template +++ auto invokeLambda( L const& lambda, std::string const& arg ) +++ -> ParserResult { +++ ArgType temp{}; +++ auto result = convertInto( arg, temp ); +++ return !result ? result +++ : LambdaInvoker::ReturnType>::invoke( lambda, temp ); +++ } +++ +++ template struct BoundLambda : BoundValueRefBase { +++ L m_lambda; +++ +++ static_assert( +++ UnaryLambdaTraits::isValid, +++ "Supplied lambda must take exactly one argument" ); +++ explicit BoundLambda( L const& lambda ): m_lambda( lambda ) {} +++ +++ auto setValue( std::string const& arg ) +++ -> ParserResult override { +++ return invokeLambda::ArgType>( +++ m_lambda, arg ); +++ } +++ }; +++ +++ template struct BoundManyLambda : BoundLambda { +++ explicit BoundManyLambda( L const& lambda ): BoundLambda( lambda ) {} +++ bool isContainer() const override { return true; } +++ }; +++ +++ template struct BoundFlagLambda : BoundFlagRefBase { +++ L m_lambda; +++ +++ static_assert( +++ UnaryLambdaTraits::isValid, +++ "Supplied lambda must take exactly one argument" ); +++ static_assert( +++ std::is_same::ArgType, +++ bool>::value, +++ "flags must be boolean" ); +++ +++ explicit BoundFlagLambda( L const& lambda ): +++ m_lambda( lambda ) {} +++ +++ auto setFlag( bool flag ) -> ParserResult override { +++ return LambdaInvoker::ReturnType>::invoke( m_lambda, flag ); +++ } +++ }; +++ +++ enum class Optionality { Optional, Required }; +++ +++ class ParserBase { +++ public: +++ virtual ~ParserBase() = default; +++ virtual auto validate() const -> Result { return Result::ok(); } +++ virtual auto parse( std::string const& exeName, +++ TokenStream tokens ) const +++ -> InternalParseResult = 0; +++ virtual size_t cardinality() const; +++ +++ InternalParseResult parse( Args const& args ) const; +++ }; +++ +++ template +++ class ComposableParserImpl : public ParserBase { +++ public: +++ template +++ auto operator|( T const& other ) const -> Parser; +++ }; +++ +++ // Common code and state for Args and Opts +++ template +++ class ParserRefImpl : public ComposableParserImpl { +++ protected: +++ Optionality m_optionality = Optionality::Optional; +++ std::shared_ptr m_ref; +++ StringRef m_hint; +++ StringRef m_description; +++ +++ explicit ParserRefImpl( std::shared_ptr const& ref ): +++ m_ref( ref ) {} +++ +++ public: +++ template +++ ParserRefImpl( accept_many_t, +++ LambdaT const& ref, +++ StringRef hint ): +++ m_ref( std::make_shared>( ref ) ), +++ m_hint( hint ) {} +++ +++ template ::value>> +++ ParserRefImpl( T& ref, StringRef hint ): +++ m_ref( std::make_shared>( ref ) ), +++ m_hint( hint ) {} +++ +++ template ::value>> +++ ParserRefImpl( LambdaT const& ref, StringRef hint ): +++ m_ref( std::make_shared>( ref ) ), +++ m_hint( hint ) {} +++ +++ DerivedT& operator()( StringRef description ) & { +++ m_description = description; +++ return static_cast( *this ); +++ } +++ DerivedT&& operator()( StringRef description ) && { +++ m_description = description; +++ return static_cast( *this ); +++ } +++ +++ auto optional() -> DerivedT& { +++ m_optionality = Optionality::Optional; +++ return static_cast( *this ); +++ } +++ +++ auto required() -> DerivedT& { +++ m_optionality = Optionality::Required; +++ return static_cast( *this ); +++ } +++ +++ auto isOptional() const -> bool { +++ return m_optionality == Optionality::Optional; +++ } +++ +++ auto cardinality() const -> size_t override { +++ if ( m_ref->isContainer() ) +++ return 0; +++ else +++ return 1; +++ } +++ +++ StringRef hint() const { return m_hint; } +++ }; +++ +++ } // namespace detail +++ +++ +++ // A parser for arguments +++ class Arg : public Detail::ParserRefImpl { +++ public: +++ using ParserRefImpl::ParserRefImpl; +++ using ParserBase::parse; +++ +++ Detail::InternalParseResult +++ parse(std::string const&, +++ Detail::TokenStream tokens) const override; +++ }; +++ +++ // A parser for options +++ class Opt : public Detail::ParserRefImpl { +++ protected: +++ std::vector m_optNames; +++ +++ public: +++ template +++ explicit Opt(LambdaT const& ref) : +++ ParserRefImpl( +++ std::make_shared>(ref)) {} +++ +++ explicit Opt(bool& ref); +++ +++ template ::value>> +++ Opt( LambdaT const& ref, StringRef hint ): +++ ParserRefImpl( ref, hint ) {} +++ +++ template +++ Opt( accept_many_t, LambdaT const& ref, StringRef hint ): +++ ParserRefImpl( accept_many, ref, hint ) {} +++ +++ template ::value>> +++ Opt( T& ref, StringRef hint ): +++ ParserRefImpl( ref, hint ) {} +++ +++ Opt& operator[]( StringRef optName ) & { +++ m_optNames.push_back(optName); +++ return *this; +++ } +++ Opt&& operator[]( StringRef optName ) && { +++ m_optNames.push_back( optName ); +++ return CATCH_MOVE(*this); +++ } +++ +++ Detail::HelpColumns getHelpColumns() const; +++ +++ bool isMatch(StringRef optToken) const; +++ +++ using ParserBase::parse; +++ +++ Detail::InternalParseResult +++ parse(std::string const&, +++ Detail::TokenStream tokens) const override; +++ +++ Detail::Result validate() const override; +++ }; +++ +++ // Specifies the name of the executable +++ class ExeName : public Detail::ComposableParserImpl { +++ std::shared_ptr m_name; +++ std::shared_ptr m_ref; +++ +++ public: +++ ExeName(); +++ explicit ExeName(std::string& ref); +++ +++ template +++ explicit ExeName(LambdaT const& lambda) : ExeName() { +++ m_ref = std::make_shared>(lambda); +++ } +++ +++ // The exe name is not parsed out of the normal tokens, but is +++ // handled specially +++ Detail::InternalParseResult +++ parse(std::string const&, +++ Detail::TokenStream tokens) const override; +++ +++ std::string const& name() const { return *m_name; } +++ Detail::ParserResult set(std::string const& newName); +++ }; +++ +++ +++ // A Combined parser +++ class Parser : Detail::ParserBase { +++ mutable ExeName m_exeName; +++ std::vector m_options; +++ std::vector m_args; +++ +++ public: +++ +++ auto operator|=(ExeName const& exeName) -> Parser& { +++ m_exeName = exeName; +++ return *this; +++ } +++ +++ auto operator|=(Arg const& arg) -> Parser& { +++ m_args.push_back(arg); +++ return *this; +++ } +++ +++ friend Parser& operator|=( Parser& p, Opt const& opt ) { +++ p.m_options.push_back( opt ); +++ return p; +++ } +++ friend Parser& operator|=( Parser& p, Opt&& opt ) { +++ p.m_options.push_back( CATCH_MOVE(opt) ); +++ return p; +++ } +++ +++ Parser& operator|=(Parser const& other); +++ +++ template +++ friend Parser operator|( Parser const& p, T&& rhs ) { +++ Parser temp( p ); +++ temp |= rhs; +++ return temp; +++ } +++ +++ template +++ friend Parser operator|( Parser&& p, T&& rhs ) { +++ p |= CATCH_FORWARD(rhs); +++ return CATCH_MOVE(p); +++ } +++ +++ std::vector getHelpColumns() const; +++ +++ void writeToStream(std::ostream& os) const; +++ +++ friend auto operator<<(std::ostream& os, Parser const& parser) +++ -> std::ostream& { +++ parser.writeToStream(os); +++ return os; +++ } +++ +++ Detail::Result validate() const override; +++ +++ using ParserBase::parse; +++ Detail::InternalParseResult +++ parse(std::string const& exeName, +++ Detail::TokenStream tokens) const override; +++ }; +++ +++ /** +++ * Wrapper over argc + argv, assumes that the inputs outlive it +++ */ +++ class Args { +++ friend Detail::TokenStream; +++ StringRef m_exeName; +++ std::vector m_args; +++ +++ public: +++ Args(int argc, char const* const* argv); +++ // Helper constructor for testing +++ Args(std::initializer_list args); +++ +++ StringRef exeName() const { return m_exeName; } +++ }; +++ +++ +++ // Convenience wrapper for option parser that specifies the help option +++ struct Help : Opt { +++ Help(bool& showHelpFlag); +++ }; +++ +++ // Result type for parser operation +++ using Detail::ParserResult; +++ +++ namespace Detail { +++ template +++ template +++ Parser +++ ComposableParserImpl::operator|(T const& other) const { +++ return Parser() | static_cast(*this) | other; +++ } +++ } +++ +++ } // namespace Clara +++} // namespace Catch +++ +++#if defined( __clang__ ) +++# pragma clang diagnostic pop +++#endif +++ +++#if defined( __GNUC__ ) +++# pragma GCC diagnostic pop +++#endif +++ +++#endif // CATCH_CLARA_HPP_INCLUDED +++ +++namespace Catch { +++ +++ struct ConfigData; +++ +++ Clara::Parser makeCommandLineParser( ConfigData& config ); +++ +++} // end namespace Catch +++ +++#endif // CATCH_COMMANDLINE_HPP_INCLUDED +++ +++namespace Catch { +++ +++ class Session : Detail::NonCopyable { +++ public: +++ +++ Session(); +++ ~Session(); +++ +++ void showHelp() const; +++ void libIdentify(); +++ +++ int applyCommandLine( int argc, char const * const * argv ); +++ #if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE) +++ int applyCommandLine( int argc, wchar_t const * const * argv ); +++ #endif +++ +++ void useConfigData( ConfigData const& configData ); +++ +++ template +++ int run(int argc, CharT const * const argv[]) { +++ if (m_startupExceptions) +++ return 1; +++ int returnCode = applyCommandLine(argc, argv); +++ if (returnCode == 0) +++ returnCode = run(); +++ return returnCode; +++ } +++ +++ int run(); +++ +++ Clara::Parser const& cli() const; +++ void cli( Clara::Parser const& newParser ); +++ ConfigData& configData(); +++ Config& config(); +++ private: +++ int runInternal(); +++ +++ Clara::Parser m_cli; +++ ConfigData m_configData; +++ Detail::unique_ptr m_config; +++ bool m_startupExceptions = false; +++ }; +++ +++} // end namespace Catch +++ +++#endif // CATCH_SESSION_HPP_INCLUDED +++ +++ +++#ifndef CATCH_TAG_ALIAS_HPP_INCLUDED +++#define CATCH_TAG_ALIAS_HPP_INCLUDED +++ +++ +++#include +++ +++namespace Catch { +++ +++ struct TagAlias { +++ TagAlias(std::string const& _tag, SourceLineInfo _lineInfo): +++ tag(_tag), +++ lineInfo(_lineInfo) +++ {} +++ +++ std::string tag; +++ SourceLineInfo lineInfo; +++ }; +++ +++} // end namespace Catch +++ +++#endif // CATCH_TAG_ALIAS_HPP_INCLUDED +++ +++ +++#ifndef CATCH_TAG_ALIAS_AUTOREGISTRAR_HPP_INCLUDED +++#define CATCH_TAG_ALIAS_AUTOREGISTRAR_HPP_INCLUDED +++ +++ +++namespace Catch { +++ +++ struct RegistrarForTagAliases { +++ RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); +++ }; +++ +++} // end namespace Catch +++ +++#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ +++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ +++ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ +++ namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ +++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +++ +++#endif // CATCH_TAG_ALIAS_AUTOREGISTRAR_HPP_INCLUDED +++ +++ +++#ifndef CATCH_TEMPLATE_TEST_MACROS_HPP_INCLUDED +++#define CATCH_TEMPLATE_TEST_MACROS_HPP_INCLUDED +++ +++// We need this suppression to leak, because it took until GCC 10 +++// for the front end to handle local suppression via _Pragma properly +++// inside templates (so `TEMPLATE_TEST_CASE` and co). +++// **THIS IS DIFFERENT FOR STANDARD TESTS, WHERE GCC 9 IS SUFFICIENT** +++#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && __GNUC__ < 10 +++#pragma GCC diagnostic ignored "-Wparentheses" +++#endif +++ +++ +++ +++ +++#ifndef CATCH_TEST_MACROS_HPP_INCLUDED +++#define CATCH_TEST_MACROS_HPP_INCLUDED +++ +++ +++ +++#ifndef CATCH_TEST_MACRO_IMPL_HPP_INCLUDED +++#define CATCH_TEST_MACRO_IMPL_HPP_INCLUDED +++ +++ +++ +++#ifndef CATCH_ASSERTION_HANDLER_HPP_INCLUDED +++#define CATCH_ASSERTION_HANDLER_HPP_INCLUDED +++ +++ +++ +++#ifndef CATCH_DECOMPOSER_HPP_INCLUDED +++#define CATCH_DECOMPOSER_HPP_INCLUDED +++ +++ +++ +++#ifndef CATCH_COMPARE_TRAITS_HPP_INCLUDED +++#define CATCH_COMPARE_TRAITS_HPP_INCLUDED +++ +++ +++#include +++ +++namespace Catch { +++ namespace Detail { +++ +++#if defined( __GNUC__ ) && !defined( __clang__ ) +++# pragma GCC diagnostic push +++ // GCC likes to complain about comparing bool with 0, in the decltype() +++ // that defines the comparable traits below. +++# pragma GCC diagnostic ignored "-Wbool-compare" +++ // "ordered comparison of pointer with integer zero" same as above, +++ // but it does not have a separate warning flag to suppress +++# pragma GCC diagnostic ignored "-Wextra" +++ // Did you know that comparing floats with `0` directly +++ // is super-duper dangerous in unevaluated context? +++# pragma GCC diagnostic ignored "-Wfloat-equal" +++#endif +++ +++#if defined( __clang__ ) +++# pragma clang diagnostic push +++ // Did you know that comparing floats with `0` directly +++ // is super-duper dangerous in unevaluated context? +++# pragma clang diagnostic ignored "-Wfloat-equal" +++#endif +++ +++#define CATCH_DEFINE_COMPARABLE_TRAIT( id, op ) \ +++ template \ +++ struct is_##id##_comparable : std::false_type {}; \ +++ template \ +++ struct is_##id##_comparable< \ +++ T, \ +++ U, \ +++ void_t() op std::declval() )>> \ +++ : std::true_type {}; \ +++ template \ +++ struct is_##id##_0_comparable : std::false_type {}; \ +++ template \ +++ struct is_##id##_0_comparable() op 0 )>> \ +++ : std::true_type {}; +++ +++ // We need all 6 pre-spaceship comparison ops: <, <=, >, >=, ==, != +++ CATCH_DEFINE_COMPARABLE_TRAIT( lt, < ) +++ CATCH_DEFINE_COMPARABLE_TRAIT( le, <= ) +++ CATCH_DEFINE_COMPARABLE_TRAIT( gt, > ) +++ CATCH_DEFINE_COMPARABLE_TRAIT( ge, >= ) +++ CATCH_DEFINE_COMPARABLE_TRAIT( eq, == ) +++ CATCH_DEFINE_COMPARABLE_TRAIT( ne, != ) +++ +++#undef CATCH_DEFINE_COMPARABLE_TRAIT +++ +++#if defined( __GNUC__ ) && !defined( __clang__ ) +++# pragma GCC diagnostic pop +++#endif +++#if defined( __clang__ ) +++# pragma clang diagnostic pop +++#endif +++ +++ +++ } // namespace Detail +++} // namespace Catch +++ +++#endif // CATCH_COMPARE_TRAITS_HPP_INCLUDED +++ +++ +++#ifndef CATCH_LOGICAL_TRAITS_HPP_INCLUDED +++#define CATCH_LOGICAL_TRAITS_HPP_INCLUDED +++ +++#include +++ +++namespace Catch { +++namespace Detail { +++ +++#if defined( __cpp_lib_logical_traits ) && __cpp_lib_logical_traits >= 201510 +++ +++ using std::conjunction; +++ using std::disjunction; +++ using std::negation; +++ +++#else +++ +++ template struct conjunction : std::true_type {}; +++ template struct conjunction : B1 {}; +++ template +++ struct conjunction +++ : std::conditional_t, B1> {}; +++ +++ template struct disjunction : std::false_type {}; +++ template struct disjunction : B1 {}; +++ template +++ struct disjunction +++ : std::conditional_t> {}; +++ +++ template +++ struct negation : std::integral_constant {}; +++ +++#endif +++ +++} // namespace Detail +++} // namespace Catch +++ +++#endif // CATCH_LOGICAL_TRAITS_HPP_INCLUDED +++ +++#include +++#include +++ +++/** \file +++ * Why does decomposing look the way it does: +++ * +++ * Conceptually, decomposing is simple. We change `REQUIRE( a == b )` into +++ * `Decomposer{} <= a == b`, so that `Decomposer{} <= a` is evaluated first, +++ * and our custom operator is used for `a == b`, because `a` is transformed +++ * into `ExprLhs` and then into `BinaryExpr`. +++ * +++ * In practice, decomposing ends up a mess, because we have to support +++ * various fun things. +++ * +++ * 1) Types that are only comparable with literal 0, and they do this by +++ * comparing against a magic type with pointer constructor and deleted +++ * other constructors. Example: `REQUIRE((a <=> b) == 0)` in libstdc++ +++ * +++ * 2) Types that are only comparable with literal 0, and they do this by +++ * comparing against a magic type with consteval integer constructor. +++ * Example: `REQUIRE((a <=> b) == 0)` in current MSVC STL. +++ * +++ * 3) Types that have no linkage, and so we cannot form a reference to +++ * them. Example: some implementations of traits. +++ * +++ * 4) Starting with C++20, when the compiler sees `a == b`, it also uses +++ * `b == a` when constructing the overload set. For us this means that +++ * when the compiler handles `ExprLhs == b`, it also tries to resolve +++ * the overload set for `b == ExprLhs`. +++ * +++ * To accomodate these use cases, decomposer ended up rather complex. +++ * +++ * 1) These types are handled by adding SFINAE overloads to our comparison +++ * operators, checking whether `T == U` are comparable with the given +++ * operator, and if not, whether T (or U) are comparable with literal 0. +++ * If yes, the overload compares T (or U) with 0 literal inline in the +++ * definition. +++ * +++ * Note that for extra correctness, we check that the other type is +++ * either an `int` (literal 0 is captured as `int` by templates), or +++ * a `long` (some platforms use 0L for `NULL` and we want to support +++ * that for pointer comparisons). +++ * +++ * 2) For these types, `is_foo_comparable` is true, but letting +++ * them fall into the overload that actually does `T == int` causes +++ * compilation error. Handling them requires that the decomposition +++ * is `constexpr`, so that P2564R3 applies and the `consteval` from +++ * their accompanying magic type is propagated through the `constexpr` +++ * call stack. +++ * +++ * However this is not enough to handle these types automatically, +++ * because our default is to capture types by reference, to avoid +++ * runtime copies. While these references cannot become dangling, +++ * they outlive the constexpr context and thus the default capture +++ * path cannot be actually constexpr. +++ * +++ * The solution is to capture these types by value, by explicitly +++ * specializing `Catch::capture_by_value` for them. Catch2 provides +++ * specialization for `std::foo_ordering`s, but users can specialize +++ * the trait for their own types as well. +++ * +++ * 3) If a type has no linkage, we also cannot capture it by reference. +++ * The solution is once again to capture them by value. We handle +++ * the common cases by using `std::is_arithmetic` as the default +++ * for `Catch::capture_by_value`, but that is only a some-effort +++ * heuristic. But as with 2), users can specialize `capture_by_value` +++ * for their own types as needed. +++ * +++ * 4) To support C++20 and make the SFINAE on our decomposing operators +++ * work, the SFINAE has to happen in return type, rather than in +++ * a template type. This is due to our use of logical type traits +++ * (`conjunction`/`disjunction`/`negation`), that we use to workaround +++ * an issue in older (9-) versions of GCC. I still blame C++20 for +++ * this, because without the comparison order switching, the logical +++ * traits could still be used in template type. +++ * +++ * There are also other side concerns, e.g. supporting both `REQUIRE(a)` +++ * and `REQUIRE(a == b)`, or making `REQUIRE_THAT(a, IsEqual(b))` slot +++ * nicely into the same expression handling logic, but these are rather +++ * straightforward and add only a bit of complexity (e.g. common base +++ * class for decomposed expressions). +++ */ +++ +++#ifdef _MSC_VER +++#pragma warning(push) +++#pragma warning(disable:4389) // '==' : signed/unsigned mismatch +++#pragma warning(disable:4018) // more "signed/unsigned mismatch" +++#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) +++#pragma warning(disable:4180) // qualifier applied to function type has no meaning +++#pragma warning(disable:4800) // Forcing result to true or false +++#endif +++ +++#ifdef __clang__ +++# pragma clang diagnostic push +++# pragma clang diagnostic ignored "-Wsign-compare" +++# pragma clang diagnostic ignored "-Wnon-virtual-dtor" +++#elif defined __GNUC__ +++# pragma GCC diagnostic push +++# pragma GCC diagnostic ignored "-Wsign-compare" +++# pragma GCC diagnostic ignored "-Wnon-virtual-dtor" +++#endif +++ +++#if defined(CATCH_CPP20_OR_GREATER) && __has_include() +++# include +++# if defined( __cpp_lib_three_way_comparison ) && \ +++ __cpp_lib_three_way_comparison >= 201907L +++# define CATCH_CONFIG_CPP20_COMPARE_OVERLOADS +++# endif +++#endif +++ +++namespace Catch { +++ +++ namespace Detail { +++ // This was added in C++20, but we require only C++14 for now. +++ template +++ using RemoveCVRef_t = std::remove_cv_t>; +++ } +++ +++ // Note: There is nothing that stops us from extending this, +++ // e.g. to `std::is_scalar`, but the more encompassing +++ // traits are usually also more expensive. For now we +++ // keep this as it used to be and it can be changed later. +++ template +++ struct capture_by_value +++ : std::integral_constant{}> {}; +++ +++#if defined( CATCH_CONFIG_CPP20_COMPARE_OVERLOADS ) +++ template <> +++ struct capture_by_value : std::true_type {}; +++ template <> +++ struct capture_by_value : std::true_type {}; +++ template <> +++ struct capture_by_value : std::true_type {}; +++#endif +++ +++ template +++ struct always_false : std::false_type {}; +++ +++ class ITransientExpression { +++ bool m_isBinaryExpression; +++ bool m_result; +++ +++ public: +++ constexpr auto isBinaryExpression() const -> bool { return m_isBinaryExpression; } +++ constexpr auto getResult() const -> bool { return m_result; } +++ //! This function **has** to be overriden by the derived class. +++ virtual void streamReconstructedExpression( std::ostream& os ) const; +++ +++ constexpr ITransientExpression( bool isBinaryExpression, bool result ) +++ : m_isBinaryExpression( isBinaryExpression ), +++ m_result( result ) +++ {} +++ +++ ITransientExpression() = default; +++ ITransientExpression(ITransientExpression const&) = default; +++ ITransientExpression& operator=(ITransientExpression const&) = default; +++ +++ friend std::ostream& operator<<(std::ostream& out, ITransientExpression const& expr) { +++ expr.streamReconstructedExpression(out); +++ return out; +++ } +++ +++ protected: +++ ~ITransientExpression() = default; +++ }; +++ +++ void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ); +++ +++ template +++ class BinaryExpr : public ITransientExpression { +++ LhsT m_lhs; +++ StringRef m_op; +++ RhsT m_rhs; +++ +++ void streamReconstructedExpression( std::ostream &os ) const override { +++ formatReconstructedExpression +++ ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) ); +++ } +++ +++ public: +++ constexpr BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) +++ : ITransientExpression{ true, comparisonResult }, +++ m_lhs( lhs ), +++ m_op( op ), +++ m_rhs( rhs ) +++ {} +++ +++ template +++ auto operator && ( T ) const -> BinaryExpr const { +++ static_assert(always_false::value, +++ "chained comparisons are not supported inside assertions, " +++ "wrap the expression inside parentheses, or decompose it"); +++ } +++ +++ template +++ auto operator || ( T ) const -> BinaryExpr const { +++ static_assert(always_false::value, +++ "chained comparisons are not supported inside assertions, " +++ "wrap the expression inside parentheses, or decompose it"); +++ } +++ +++ template +++ auto operator == ( T ) const -> BinaryExpr const { +++ static_assert(always_false::value, +++ "chained comparisons are not supported inside assertions, " +++ "wrap the expression inside parentheses, or decompose it"); +++ } +++ +++ template +++ auto operator != ( T ) const -> BinaryExpr const { +++ static_assert(always_false::value, +++ "chained comparisons are not supported inside assertions, " +++ "wrap the expression inside parentheses, or decompose it"); +++ } +++ +++ template +++ auto operator > ( T ) const -> BinaryExpr const { +++ static_assert(always_false::value, +++ "chained comparisons are not supported inside assertions, " +++ "wrap the expression inside parentheses, or decompose it"); +++ } +++ +++ template +++ auto operator < ( T ) const -> BinaryExpr const { +++ static_assert(always_false::value, +++ "chained comparisons are not supported inside assertions, " +++ "wrap the expression inside parentheses, or decompose it"); +++ } +++ +++ template +++ auto operator >= ( T ) const -> BinaryExpr const { +++ static_assert(always_false::value, +++ "chained comparisons are not supported inside assertions, " +++ "wrap the expression inside parentheses, or decompose it"); +++ } +++ +++ template +++ auto operator <= ( T ) const -> BinaryExpr const { +++ static_assert(always_false::value, +++ "chained comparisons are not supported inside assertions, " +++ "wrap the expression inside parentheses, or decompose it"); +++ } +++ }; +++ +++ template +++ class UnaryExpr : public ITransientExpression { +++ LhsT m_lhs; +++ +++ void streamReconstructedExpression( std::ostream &os ) const override { +++ os << Catch::Detail::stringify( m_lhs ); +++ } +++ +++ public: +++ explicit constexpr UnaryExpr( LhsT lhs ) +++ : ITransientExpression{ false, static_cast(lhs) }, +++ m_lhs( lhs ) +++ {} +++ }; +++ +++ +++ template +++ class ExprLhs { +++ LhsT m_lhs; +++ public: +++ explicit constexpr ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} +++ +++#define CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( id, op ) \ +++ template \ +++ constexpr friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \ +++ -> std::enable_if_t< \ +++ Detail::conjunction, \ +++ Detail::negation>>>::value, \ +++ BinaryExpr> { \ +++ return { \ +++ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ +++ } \ +++ template \ +++ constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ +++ -> std::enable_if_t< \ +++ Detail::conjunction, \ +++ capture_by_value>::value, \ +++ BinaryExpr> { \ +++ return { \ +++ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ +++ } \ +++ template \ +++ constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ +++ -> std::enable_if_t< \ +++ Detail::conjunction< \ +++ Detail::negation>, \ +++ Detail::is_eq_0_comparable, \ +++ /* We allow long because we want `ptr op NULL` to be accepted */ \ +++ Detail::disjunction, \ +++ std::is_same>>::value, \ +++ BinaryExpr> { \ +++ if ( rhs != 0 ) { throw_test_failure_exception(); } \ +++ return { \ +++ static_cast( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \ +++ } \ +++ template \ +++ constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ +++ -> std::enable_if_t< \ +++ Detail::conjunction< \ +++ Detail::negation>, \ +++ Detail::is_eq_0_comparable, \ +++ /* We allow long because we want `ptr op NULL` to be accepted */ \ +++ Detail::disjunction, \ +++ std::is_same>>::value, \ +++ BinaryExpr> { \ +++ if ( lhs.m_lhs != 0 ) { throw_test_failure_exception(); } \ +++ return { static_cast( 0 op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ +++ } +++ +++ CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( eq, == ) +++ CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( ne, != ) +++ +++ #undef CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR +++ +++ +++#define CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( id, op ) \ +++ template \ +++ constexpr friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \ +++ -> std::enable_if_t< \ +++ Detail::conjunction, \ +++ Detail::negation>>>::value, \ +++ BinaryExpr> { \ +++ return { \ +++ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ +++ } \ +++ template \ +++ constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ +++ -> std::enable_if_t< \ +++ Detail::conjunction, \ +++ capture_by_value>::value, \ +++ BinaryExpr> { \ +++ return { \ +++ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ +++ } \ +++ template \ +++ constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ +++ -> std::enable_if_t< \ +++ Detail::conjunction< \ +++ Detail::negation>, \ +++ Detail::is_##id##_0_comparable, \ +++ std::is_same>::value, \ +++ BinaryExpr> { \ +++ if ( rhs != 0 ) { throw_test_failure_exception(); } \ +++ return { \ +++ static_cast( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \ +++ } \ +++ template \ +++ constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ +++ -> std::enable_if_t< \ +++ Detail::conjunction< \ +++ Detail::negation>, \ +++ Detail::is_##id##_0_comparable, \ +++ std::is_same>::value, \ +++ BinaryExpr> { \ +++ if ( lhs.m_lhs != 0 ) { throw_test_failure_exception(); } \ +++ return { static_cast( 0 op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ +++ } +++ +++ CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( lt, < ) +++ CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( le, <= ) +++ CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( gt, > ) +++ CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( ge, >= ) +++ +++ #undef CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR +++ +++ +++#define CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR( op ) \ +++ template \ +++ constexpr friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \ +++ -> std::enable_if_t< \ +++ !capture_by_value>::value, \ +++ BinaryExpr> { \ +++ return { \ +++ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ +++ } \ +++ template \ +++ constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ +++ -> std::enable_if_t::value, \ +++ BinaryExpr> { \ +++ return { \ +++ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ +++ } +++ +++ CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(|) +++ CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(&) +++ CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(^) +++ +++ #undef CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR +++ +++ template +++ friend auto operator && ( ExprLhs &&, RhsT && ) -> BinaryExpr { +++ static_assert(always_false::value, +++ "operator&& is not supported inside assertions, " +++ "wrap the expression inside parentheses, or decompose it"); +++ } +++ +++ template +++ friend auto operator || ( ExprLhs &&, RhsT && ) -> BinaryExpr { +++ static_assert(always_false::value, +++ "operator|| is not supported inside assertions, " +++ "wrap the expression inside parentheses, or decompose it"); +++ } +++ +++ constexpr auto makeUnaryExpr() const -> UnaryExpr { +++ return UnaryExpr{ m_lhs }; +++ } +++ }; +++ +++ struct Decomposer { +++ template >::value, +++ int> = 0> +++ constexpr friend auto operator <= ( Decomposer &&, T && lhs ) -> ExprLhs { +++ return ExprLhs{ lhs }; +++ } +++ +++ template ::value, int> = 0> +++ constexpr friend auto operator <= ( Decomposer &&, T value ) -> ExprLhs { +++ return ExprLhs{ value }; +++ } +++ }; +++ +++} // end namespace Catch +++ +++#ifdef _MSC_VER +++#pragma warning(pop) +++#endif +++#ifdef __clang__ +++# pragma clang diagnostic pop +++#elif defined __GNUC__ +++# pragma GCC diagnostic pop +++#endif +++ +++#endif // CATCH_DECOMPOSER_HPP_INCLUDED +++ +++#include +++ +++namespace Catch { +++ +++ struct AssertionReaction { +++ bool shouldDebugBreak = false; +++ bool shouldThrow = false; +++ bool shouldSkip = false; +++ }; +++ +++ class AssertionHandler { +++ AssertionInfo m_assertionInfo; +++ AssertionReaction m_reaction; +++ bool m_completed = false; +++ IResultCapture& m_resultCapture; +++ +++ public: +++ AssertionHandler +++ ( StringRef macroName, +++ SourceLineInfo const& lineInfo, +++ StringRef capturedExpression, +++ ResultDisposition::Flags resultDisposition ); +++ ~AssertionHandler() { +++ if ( !m_completed ) { +++ m_resultCapture.handleIncomplete( m_assertionInfo ); +++ } +++ } +++ +++ +++ template +++ void handleExpr( ExprLhs const& expr ) { +++ handleExpr( expr.makeUnaryExpr() ); +++ } +++ void handleExpr( ITransientExpression const& expr ); +++ +++ void handleMessage(ResultWas::OfType resultType, StringRef message); +++ +++ void handleExceptionThrownAsExpected(); +++ void handleUnexpectedExceptionNotThrown(); +++ void handleExceptionNotThrownAsExpected(); +++ void handleThrowingCallSkipped(); +++ void handleUnexpectedInflightException(); +++ +++ void complete(); +++ +++ // query +++ auto allowThrows() const -> bool; +++ }; +++ +++ void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str ); +++ +++} // namespace Catch +++ +++#endif // CATCH_ASSERTION_HANDLER_HPP_INCLUDED +++ +++ +++#ifndef CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED +++#define CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED +++ +++ +++#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) +++ #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__##_catch_sr +++#else +++ #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION"_catch_sr +++#endif +++ +++#endif // CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED +++ +++// We need this suppression to leak, because it took until GCC 10 +++// for the front end to handle local suppression via _Pragma properly +++#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && __GNUC__ <= 9 +++ #pragma GCC diagnostic ignored "-Wparentheses" +++#endif +++ +++#if !defined(CATCH_CONFIG_DISABLE) +++ +++#if defined(CATCH_CONFIG_FAST_COMPILE) || defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +++ +++/////////////////////////////////////////////////////////////////////////////// +++// Another way to speed-up compilation is to omit local try-catch for REQUIRE* +++// macros. +++#define INTERNAL_CATCH_TRY +++#define INTERNAL_CATCH_CATCH( capturer ) +++ +++#else // CATCH_CONFIG_FAST_COMPILE +++ +++#define INTERNAL_CATCH_TRY try +++#define INTERNAL_CATCH_CATCH( handler ) catch(...) { (handler).handleUnexpectedInflightException(); } +++ +++#endif +++ +++#define INTERNAL_CATCH_REACT( handler ) handler.complete(); +++ +++/////////////////////////////////////////////////////////////////////////////// +++#define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \ +++ do { /* NOLINT(bugprone-infinite-loop) */ \ +++ /* The expression should not be evaluated, but warnings should hopefully be checked */ \ +++ CATCH_INTERNAL_IGNORE_BUT_WARN(__VA_ARGS__); \ +++ Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ +++ INTERNAL_CATCH_TRY { \ +++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ +++ CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ +++ catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); /* NOLINT(bugprone-chained-comparison) */ \ +++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ +++ } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ +++ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ +++ } while( (void)0, (false) && static_cast( !!(__VA_ARGS__) ) ) // the expression here is never evaluated at runtime but it forces the compiler to give it a look +++ // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. +++ +++/////////////////////////////////////////////////////////////////////////////// +++#define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \ +++ INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ +++ if( Catch::getResultCapture().lastAssertionPassed() ) +++ +++/////////////////////////////////////////////////////////////////////////////// +++#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, ... ) \ +++ INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ +++ if( !Catch::getResultCapture().lastAssertionPassed() ) +++ +++/////////////////////////////////////////////////////////////////////////////// +++#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, ... ) \ +++ do { \ +++ Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ +++ try { \ +++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ +++ CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ +++ static_cast(__VA_ARGS__); \ +++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ +++ catchAssertionHandler.handleExceptionNotThrownAsExpected(); \ +++ } \ +++ catch( ... ) { \ +++ catchAssertionHandler.handleUnexpectedInflightException(); \ +++ } \ +++ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ +++ } while( false ) +++ +++/////////////////////////////////////////////////////////////////////////////// +++#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, ... ) \ +++ do { \ +++ Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition); \ +++ if( catchAssertionHandler.allowThrows() ) \ +++ try { \ +++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ +++ CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ +++ CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ +++ static_cast(__VA_ARGS__); \ +++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ +++ catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ +++ } \ +++ catch( ... ) { \ +++ catchAssertionHandler.handleExceptionThrownAsExpected(); \ +++ } \ +++ else \ +++ catchAssertionHandler.handleThrowingCallSkipped(); \ +++ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ +++ } while( false ) +++ +++/////////////////////////////////////////////////////////////////////////////// +++#define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \ +++ do { \ +++ Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \ +++ if( catchAssertionHandler.allowThrows() ) \ +++ try { \ +++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ +++ CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ +++ CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ +++ static_cast(expr); \ +++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ +++ catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ +++ } \ +++ catch( exceptionType const& ) { \ +++ catchAssertionHandler.handleExceptionThrownAsExpected(); \ +++ } \ +++ catch( ... ) { \ +++ catchAssertionHandler.handleUnexpectedInflightException(); \ +++ } \ +++ else \ +++ catchAssertionHandler.handleThrowingCallSkipped(); \ +++ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ +++ } while( false ) +++ +++ +++ +++/////////////////////////////////////////////////////////////////////////////// +++// Although this is matcher-based, it can be used with just a string +++#define INTERNAL_CATCH_THROWS_STR_MATCHES( macroName, resultDisposition, matcher, ... ) \ +++ do { \ +++ Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ +++ if( catchAssertionHandler.allowThrows() ) \ +++ try { \ +++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ +++ CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ +++ CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ +++ static_cast(__VA_ARGS__); \ +++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ +++ catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ +++ } \ +++ catch( ... ) { \ +++ Catch::handleExceptionMatchExpr( catchAssertionHandler, matcher ); \ +++ } \ +++ else \ +++ catchAssertionHandler.handleThrowingCallSkipped(); \ +++ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ +++ } while( false ) +++ +++#endif // CATCH_CONFIG_DISABLE +++ +++#endif // CATCH_TEST_MACRO_IMPL_HPP_INCLUDED +++ +++ +++#ifndef CATCH_SECTION_HPP_INCLUDED +++#define CATCH_SECTION_HPP_INCLUDED +++ +++ +++ +++ +++/** \file +++ * Wrapper for the STATIC_ANALYSIS_SUPPORT configuration option +++ * +++ * Some of Catch2's macros can be defined differently to work better with +++ * static analysis tools, like clang-tidy or coverity. +++ * Currently the main use case is to show that `SECTION`s are executed +++ * exclusively, and not all in one run of a `TEST_CASE`. +++ */ +++ +++#ifndef CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED +++#define CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED +++ +++ +++#if defined(__clang_analyzer__) || defined(__COVERITY__) +++ #define CATCH_INTERNAL_CONFIG_STATIC_ANALYSIS_SUPPORT +++#endif +++ +++#if defined( CATCH_INTERNAL_CONFIG_STATIC_ANALYSIS_SUPPORT ) && \ +++ !defined( CATCH_CONFIG_NO_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT ) && \ +++ !defined( CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT ) +++# define CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT +++#endif +++ +++ +++#endif // CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED +++ +++ +++#ifndef CATCH_TIMER_HPP_INCLUDED +++#define CATCH_TIMER_HPP_INCLUDED +++ +++#include +++ +++namespace Catch { +++ +++ class Timer { +++ uint64_t m_nanoseconds = 0; +++ public: +++ void start(); +++ auto getElapsedNanoseconds() const -> uint64_t; +++ auto getElapsedMicroseconds() const -> uint64_t; +++ auto getElapsedMilliseconds() const -> unsigned int; +++ auto getElapsedSeconds() const -> double; +++ }; +++ +++} // namespace Catch +++ +++#endif // CATCH_TIMER_HPP_INCLUDED +++ +++namespace Catch { +++ +++ class Section : Detail::NonCopyable { +++ public: +++ Section( SectionInfo&& info ); +++ Section( SourceLineInfo const& _lineInfo, +++ StringRef _name, +++ const char* const = nullptr ); +++ ~Section(); +++ +++ // This indicates whether the section should be executed or not +++ explicit operator bool() const; +++ +++ private: +++ SectionInfo m_info; +++ +++ Counts m_assertions; +++ bool m_sectionIncluded; +++ Timer m_timer; +++ }; +++ +++} // end namespace Catch +++ +++#if !defined(CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT) +++# define INTERNAL_CATCH_SECTION( ... ) \ +++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ +++ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ +++ if ( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( \ +++ catch_internal_Section ) = \ +++ Catch::Section( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \ +++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +++ +++# define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ +++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ +++ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ +++ if ( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( \ +++ catch_internal_Section ) = \ +++ Catch::SectionInfo( \ +++ CATCH_INTERNAL_LINEINFO, \ +++ ( Catch::ReusableStringStream() << __VA_ARGS__ ) \ +++ .str() ) ) \ +++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +++ +++#else +++ +++// These section definitions imply that at most one section at one level +++// will be intered (because only one section's __LINE__ can be equal to +++// the dummy `catchInternalSectionHint` variable from `TEST_CASE`). +++ +++namespace Catch { +++ namespace Detail { +++ // Intentionally without linkage, as it should only be used as a dummy +++ // symbol for static analysis. +++ // The arguments are used as a dummy for checking warnings in the passed +++ // expressions. +++ int GetNewSectionHint( StringRef, const char* const = nullptr ); +++ } // namespace Detail +++} // namespace Catch +++ +++ +++# define INTERNAL_CATCH_SECTION( ... ) \ +++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ +++ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ +++ CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ +++ if ( [[maybe_unused]] const int catchInternalPreviousSectionHint = \ +++ catchInternalSectionHint, \ +++ catchInternalSectionHint = \ +++ Catch::Detail::GetNewSectionHint(__VA_ARGS__); \ +++ catchInternalPreviousSectionHint == __LINE__ ) \ +++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +++ +++# define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ +++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ +++ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ +++ CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ +++ if ( [[maybe_unused]] const int catchInternalPreviousSectionHint = \ +++ catchInternalSectionHint, \ +++ catchInternalSectionHint = Catch::Detail::GetNewSectionHint( \ +++ ( Catch::ReusableStringStream() << __VA_ARGS__ ).str()); \ +++ catchInternalPreviousSectionHint == __LINE__ ) \ +++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +++ +++#endif +++ +++ +++#endif // CATCH_SECTION_HPP_INCLUDED +++ +++ +++#ifndef CATCH_TEST_REGISTRY_HPP_INCLUDED +++#define CATCH_TEST_REGISTRY_HPP_INCLUDED +++ +++ +++ +++#ifndef CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED +++#define CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED +++ +++namespace Catch { +++ +++ class ITestInvoker { +++ public: +++ virtual void invoke() const = 0; +++ virtual ~ITestInvoker(); // = default +++ }; +++ +++} // namespace Catch +++ +++#endif // CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED +++ +++ +++#ifndef CATCH_PREPROCESSOR_REMOVE_PARENS_HPP_INCLUDED +++#define CATCH_PREPROCESSOR_REMOVE_PARENS_HPP_INCLUDED +++ +++#define INTERNAL_CATCH_EXPAND1( param ) INTERNAL_CATCH_EXPAND2( param ) +++#define INTERNAL_CATCH_EXPAND2( ... ) INTERNAL_CATCH_NO##__VA_ARGS__ +++#define INTERNAL_CATCH_DEF( ... ) INTERNAL_CATCH_DEF __VA_ARGS__ +++#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +++ +++#define INTERNAL_CATCH_REMOVE_PARENS( ... ) \ +++ INTERNAL_CATCH_EXPAND1( INTERNAL_CATCH_DEF __VA_ARGS__ ) +++ +++#endif // CATCH_PREPROCESSOR_REMOVE_PARENS_HPP_INCLUDED +++ +++// GCC 5 and older do not properly handle disabling unused-variable warning +++// with a _Pragma. This means that we have to leak the suppression to the +++// user code as well :-( +++#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 5 +++#pragma GCC diagnostic ignored "-Wunused-variable" +++#endif +++ +++ +++ +++namespace Catch { +++ +++template +++class TestInvokerAsMethod : public ITestInvoker { +++ void (C::*m_testAsMethod)(); +++public: +++ TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {} +++ +++ void invoke() const override { +++ C obj; +++ (obj.*m_testAsMethod)(); +++ } +++}; +++ +++Detail::unique_ptr makeTestInvoker( void(*testAsFunction)() ); +++ +++template +++Detail::unique_ptr makeTestInvoker( void (C::*testAsMethod)() ) { +++ return Detail::make_unique>( testAsMethod ); +++} +++ +++struct NameAndTags { +++ constexpr NameAndTags( StringRef name_ = StringRef(), +++ StringRef tags_ = StringRef() ) noexcept: +++ name( name_ ), tags( tags_ ) {} +++ StringRef name; +++ StringRef tags; +++}; +++ +++struct AutoReg : Detail::NonCopyable { +++ AutoReg( Detail::unique_ptr invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept; +++}; +++ +++} // end namespace Catch +++ +++#if defined(CATCH_CONFIG_DISABLE) +++ #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \ +++ static inline void TestName() +++ #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \ +++ namespace{ \ +++ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ +++ void test(); \ +++ }; \ +++ } \ +++ void TestName::test() +++#endif +++ +++ +++#if !defined(CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT) +++ +++ /////////////////////////////////////////////////////////////////////////////// +++ #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ +++ static void TestName(); \ +++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ +++ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ +++ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ +++ namespace{ const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ +++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ +++ static void TestName() +++ #define INTERNAL_CATCH_TESTCASE( ... ) \ +++ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__ ) +++ +++#else // ^^ !CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT | vv CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT +++ +++ +++// Dummy registrator for the dumy test case macros +++namespace Catch { +++ namespace Detail { +++ struct DummyUse { +++ DummyUse( void ( * )( int ), Catch::NameAndTags const& ); +++ }; +++ } // namespace Detail +++} // namespace Catch +++ +++// Note that both the presence of the argument and its exact name are +++// necessary for the section support. +++ +++// We provide a shadowed variable so that a `SECTION` inside non-`TEST_CASE` +++// tests can compile. The redefined `TEST_CASE` shadows this with param. +++static int catchInternalSectionHint = 0; +++ +++# define INTERNAL_CATCH_TESTCASE2( fname, ... ) \ +++ static void fname( int ); \ +++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ +++ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ +++ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ +++ static const Catch::Detail::DummyUse INTERNAL_CATCH_UNIQUE_NAME( \ +++ dummyUser )( &(fname), Catch::NameAndTags{ __VA_ARGS__ } ); \ +++ CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ +++ static void fname( [[maybe_unused]] int catchInternalSectionHint ) \ +++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +++# define INTERNAL_CATCH_TESTCASE( ... ) \ +++ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( dummyFunction ), __VA_ARGS__ ) +++ +++ +++#endif // CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT +++ +++ /////////////////////////////////////////////////////////////////////////////// +++ #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ +++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ +++ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ +++ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ +++ namespace{ \ +++ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ +++ void test(); \ +++ }; \ +++ const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \ +++ Catch::makeTestInvoker( &TestName::test ), \ +++ CATCH_INTERNAL_LINEINFO, \ +++ #ClassName##_catch_sr, \ +++ Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ +++ } \ +++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ +++ void TestName::test() +++ #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ +++ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), ClassName, __VA_ARGS__ ) +++ +++ +++ /////////////////////////////////////////////////////////////////////////////// +++ #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ +++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ +++ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ +++ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ +++ namespace { \ +++ const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \ +++ Catch::makeTestInvoker( &QualifiedMethod ), \ +++ CATCH_INTERNAL_LINEINFO, \ +++ "&" #QualifiedMethod##_catch_sr, \ +++ Catch::NameAndTags{ __VA_ARGS__ } ); \ +++ } /* NOLINT */ \ +++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +++ +++ +++ /////////////////////////////////////////////////////////////////////////////// +++ #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ +++ do { \ +++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ +++ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ +++ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ +++ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ +++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ +++ } while(false) +++ +++ +++#endif // CATCH_TEST_REGISTRY_HPP_INCLUDED +++ +++ +++// All of our user-facing macros support configuration toggle, that +++// forces them to be defined prefixed with CATCH_. We also like to +++// support another toggle that can minimize (disable) their implementation. +++// Given this, we have 4 different configuration options below +++ +++#if defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE) +++ +++ #define CATCH_REQUIRE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +++ #define CATCH_REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) +++ +++ #define CATCH_REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +++ #define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) +++ #define CATCH_REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +++ +++ #define CATCH_CHECK( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +++ #define CATCH_CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) +++ #define CATCH_CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) +++ #define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) +++ #define CATCH_CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) +++ +++ #define CATCH_CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +++ #define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) +++ #define CATCH_CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +++ +++ #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) +++ #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) +++ #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) +++ #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) +++ #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) +++ #define CATCH_DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ ) +++ #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) +++ #define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +++ #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +++ #define CATCH_SKIP( ... ) INTERNAL_CATCH_MSG( "SKIP", Catch::ResultWas::ExplicitSkip, Catch::ResultDisposition::Normal, __VA_ARGS__ ) +++ +++ +++ #if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) +++ #define CATCH_STATIC_REQUIRE( ... ) static_assert( __VA_ARGS__ , #__VA_ARGS__ ); CATCH_SUCCEED( #__VA_ARGS__ ) +++ #define CATCH_STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); CATCH_SUCCEED( #__VA_ARGS__ ) +++ #define CATCH_STATIC_CHECK( ... ) static_assert( __VA_ARGS__ , #__VA_ARGS__ ); CATCH_SUCCEED( #__VA_ARGS__ ) +++ #define CATCH_STATIC_CHECK_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); CATCH_SUCCEED( #__VA_ARGS__ ) +++ #else +++ #define CATCH_STATIC_REQUIRE( ... ) CATCH_REQUIRE( __VA_ARGS__ ) +++ #define CATCH_STATIC_REQUIRE_FALSE( ... ) CATCH_REQUIRE_FALSE( __VA_ARGS__ ) +++ #define CATCH_STATIC_CHECK( ... ) CATCH_CHECK( __VA_ARGS__ ) +++ #define CATCH_STATIC_CHECK_FALSE( ... ) CATCH_CHECK_FALSE( __VA_ARGS__ ) +++ #endif +++ +++ +++ // "BDD-style" convenience wrappers +++ #define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) +++ #define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) +++ #define CATCH_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Given: " << desc ) +++ #define CATCH_AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc ) +++ #define CATCH_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " When: " << desc ) +++ #define CATCH_AND_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc ) +++ #define CATCH_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc ) +++ #define CATCH_AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc ) +++ +++#elif defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE) // ^^ prefixed, implemented | vv prefixed, disabled +++ +++ #define CATCH_REQUIRE( ... ) (void)(0) +++ #define CATCH_REQUIRE_FALSE( ... ) (void)(0) +++ +++ #define CATCH_REQUIRE_THROWS( ... ) (void)(0) +++ #define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) +++ #define CATCH_REQUIRE_NOTHROW( ... ) (void)(0) +++ +++ #define CATCH_CHECK( ... ) (void)(0) +++ #define CATCH_CHECK_FALSE( ... ) (void)(0) +++ #define CATCH_CHECKED_IF( ... ) if (__VA_ARGS__) +++ #define CATCH_CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) +++ #define CATCH_CHECK_NOFAIL( ... ) (void)(0) +++ +++ #define CATCH_CHECK_THROWS( ... ) (void)(0) +++ #define CATCH_CHECK_THROWS_AS( expr, exceptionType ) (void)(0) +++ #define CATCH_CHECK_NOTHROW( ... ) (void)(0) +++ +++ #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) +++ #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) +++ #define CATCH_METHOD_AS_TEST_CASE( method, ... ) +++ #define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0) +++ #define CATCH_SECTION( ... ) +++ #define CATCH_DYNAMIC_SECTION( ... ) +++ #define CATCH_FAIL( ... ) (void)(0) +++ #define CATCH_FAIL_CHECK( ... ) (void)(0) +++ #define CATCH_SUCCEED( ... ) (void)(0) +++ #define CATCH_SKIP( ... ) (void)(0) +++ +++ #define CATCH_STATIC_REQUIRE( ... ) (void)(0) +++ #define CATCH_STATIC_REQUIRE_FALSE( ... ) (void)(0) +++ #define CATCH_STATIC_CHECK( ... ) (void)(0) +++ #define CATCH_STATIC_CHECK_FALSE( ... ) (void)(0) +++ +++ // "BDD-style" convenience wrappers +++ #define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) +++ #define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), className ) +++ #define CATCH_GIVEN( desc ) +++ #define CATCH_AND_GIVEN( desc ) +++ #define CATCH_WHEN( desc ) +++ #define CATCH_AND_WHEN( desc ) +++ #define CATCH_THEN( desc ) +++ #define CATCH_AND_THEN( desc ) +++ +++#elif !defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE) // ^^ prefixed, disabled | vv unprefixed, implemented +++ +++ #define REQUIRE( ... ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +++ #define REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) +++ +++ #define REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +++ #define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) +++ #define REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +++ +++ #define CHECK( ... ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +++ #define CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) +++ #define CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) +++ #define CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) +++ #define CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) +++ +++ #define CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +++ #define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) +++ #define CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +++ +++ #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) +++ #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) +++ #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) +++ #define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) +++ #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) +++ #define DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ ) +++ #define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) +++ #define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +++ #define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +++ #define SKIP( ... ) INTERNAL_CATCH_MSG( "SKIP", Catch::ResultWas::ExplicitSkip, Catch::ResultDisposition::Normal, __VA_ARGS__ ) +++ +++ +++ #if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) +++ #define STATIC_REQUIRE( ... ) static_assert( __VA_ARGS__, #__VA_ARGS__ ); SUCCEED( #__VA_ARGS__ ) +++ #define STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); SUCCEED( "!(" #__VA_ARGS__ ")" ) +++ #define STATIC_CHECK( ... ) static_assert( __VA_ARGS__, #__VA_ARGS__ ); SUCCEED( #__VA_ARGS__ ) +++ #define STATIC_CHECK_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); SUCCEED( "!(" #__VA_ARGS__ ")" ) +++ #else +++ #define STATIC_REQUIRE( ... ) REQUIRE( __VA_ARGS__ ) +++ #define STATIC_REQUIRE_FALSE( ... ) REQUIRE_FALSE( __VA_ARGS__ ) +++ #define STATIC_CHECK( ... ) CHECK( __VA_ARGS__ ) +++ #define STATIC_CHECK_FALSE( ... ) CHECK_FALSE( __VA_ARGS__ ) +++ #endif +++ +++ // "BDD-style" convenience wrappers +++ #define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) +++ #define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) +++ #define GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Given: " << desc ) +++ #define AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc ) +++ #define WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " When: " << desc ) +++ #define AND_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc ) +++ #define THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc ) +++ #define AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc ) +++ +++#elif !defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE) // ^^ unprefixed, implemented | vv unprefixed, disabled +++ +++ #define REQUIRE( ... ) (void)(0) +++ #define REQUIRE_FALSE( ... ) (void)(0) +++ +++ #define REQUIRE_THROWS( ... ) (void)(0) +++ #define REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) +++ #define REQUIRE_NOTHROW( ... ) (void)(0) +++ +++ #define CHECK( ... ) (void)(0) +++ #define CHECK_FALSE( ... ) (void)(0) +++ #define CHECKED_IF( ... ) if (__VA_ARGS__) +++ #define CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) +++ #define CHECK_NOFAIL( ... ) (void)(0) +++ +++ #define CHECK_THROWS( ... ) (void)(0) +++ #define CHECK_THROWS_AS( expr, exceptionType ) (void)(0) +++ #define CHECK_NOTHROW( ... ) (void)(0) +++ +++ #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__) +++ #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) +++ #define METHOD_AS_TEST_CASE( method, ... ) +++ #define REGISTER_TEST_CASE( Function, ... ) (void)(0) +++ #define SECTION( ... ) +++ #define DYNAMIC_SECTION( ... ) +++ #define FAIL( ... ) (void)(0) +++ #define FAIL_CHECK( ... ) (void)(0) +++ #define SUCCEED( ... ) (void)(0) +++ #define SKIP( ... ) (void)(0) +++ +++ #define STATIC_REQUIRE( ... ) (void)(0) +++ #define STATIC_REQUIRE_FALSE( ... ) (void)(0) +++ #define STATIC_CHECK( ... ) (void)(0) +++ #define STATIC_CHECK_FALSE( ... ) (void)(0) +++ +++ // "BDD-style" convenience wrappers +++ #define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ) ) +++ #define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), className ) +++ +++ #define GIVEN( desc ) +++ #define AND_GIVEN( desc ) +++ #define WHEN( desc ) +++ #define AND_WHEN( desc ) +++ #define THEN( desc ) +++ #define AND_THEN( desc ) +++ +++#endif // ^^ unprefixed, disabled +++ +++// end of user facing macros +++ +++#endif // CATCH_TEST_MACROS_HPP_INCLUDED +++ +++ +++#ifndef CATCH_TEMPLATE_TEST_REGISTRY_HPP_INCLUDED +++#define CATCH_TEMPLATE_TEST_REGISTRY_HPP_INCLUDED +++ +++ +++ +++#ifndef CATCH_PREPROCESSOR_HPP_INCLUDED +++#define CATCH_PREPROCESSOR_HPP_INCLUDED +++ +++ +++#if defined(__GNUC__) +++// We need to silence "empty __VA_ARGS__ warning", and using just _Pragma does not work +++#pragma GCC system_header +++#endif +++ +++ +++#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ +++#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) +++#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) +++#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) +++#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) +++#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) +++ +++#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +++#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ +++// MSVC needs more evaluations +++#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) +++#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) +++#else +++#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) +++#endif +++ +++#define CATCH_REC_END(...) +++#define CATCH_REC_OUT +++ +++#define CATCH_EMPTY() +++#define CATCH_DEFER(id) id CATCH_EMPTY() +++ +++#define CATCH_REC_GET_END2() 0, CATCH_REC_END +++#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 +++#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 +++#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT +++#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) +++#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) +++ +++#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) +++#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) +++#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) +++ +++#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +++#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +++#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +++ +++// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, +++// and passes userdata as the first parameter to each invocation, +++// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) +++#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) +++ +++#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) +++ +++#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) +++#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +++#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ +++#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) +++#else +++// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +++#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) +++#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ +++#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) +++#endif +++ +++#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ +++#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) +++ +++#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +++#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) +++#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) +++#else +++#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) +++#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) +++#endif +++ +++#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ +++ CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) +++ +++#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) +++#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) +++#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) +++#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) +++#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) +++#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) +++#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) +++#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) +++#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) +++#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) +++#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) +++ +++#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N +++ +++#define INTERNAL_CATCH_TYPE_GEN\ +++ template struct TypeList {};\ +++ template\ +++ constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ +++ template class...> struct TemplateTypeList{};\ +++ template class...Cs>\ +++ constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ +++ template\ +++ struct append;\ +++ template\ +++ struct rewrap;\ +++ template class, typename...>\ +++ struct create;\ +++ template class, typename>\ +++ struct convert;\ +++ \ +++ template \ +++ struct append { using type = T; };\ +++ template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ +++ struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ +++ template< template class L1, typename...E1, typename...Rest>\ +++ struct append, TypeList, Rest...> { using type = L1; };\ +++ \ +++ template< template class Container, template class List, typename...elems>\ +++ struct rewrap, List> { using type = TypeList>; };\ +++ template< template class Container, template class List, class...Elems, typename...Elements>\ +++ struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ +++ \ +++ template